diff options
author | Jorge Ruesga <jorge@ruesga.com> | 2013-03-25 04:56:38 +0100 |
---|---|---|
committer | Jorge Ruesga <jorge@ruesga.com> | 2013-03-27 00:16:04 +0100 |
commit | 8573124b80d19a9562a6e623022171b41dc4d183 (patch) | |
tree | 40aa0eca71ca3bd4e09abee5efa046f31cc2f308 | |
parent | d063637d6110ab2a762268d7ef20afeaa6f482af (diff) | |
download | android_packages_apps_CMFileManager-8573124b80d19a9562a6e623022171b41dc4d183.tar.gz android_packages_apps_CMFileManager-8573124b80d19a9562a6e623022171b41dc4d183.tar.bz2 android_packages_apps_CMFileManager-8573124b80d19a9562a6e623022171b41dc4d183.zip |
CMFM: Editor initial highlight support + props syntax processor + others features
- This change enables support of syntax highlight in editor. Adds also the next syntax processors:
* PropertiesSyntaxHighlightProcessor
- Option for toggle "no suggestion" in editor
- CleanUp
Patchset 2: Theme color scheme support
Patchset 3: Hexdump binary editor preference
No suggestions editor preference
Syntax Highlight color scheme
Add android-syntax-highlight (CMFM) and color-picker-view libraries
ColorPickerDialog and ColorPickerPreference
Separate preference to its own file
Extract themes strings from dark_theme.xml to strings.xml
Rebased
Change-Id: I9df65e6193d46ebafadee5d545dcde1fc5ce20e9
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
47 files changed, 5007 insertions, 523 deletions
@@ -19,6 +19,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_SRC_FILES += $(call all-java-files-under, themes/src) +LOCAL_SRC_FILES += $(call all-java-files-under, libs/android-syntax-highlight/src) +LOCAL_SRC_FILES += $(call all-java-files-under, libs/color-picker-view/src) LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, themes/res res) LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true LOCAL_AAPT_FLAGS := --auto-add-overlay @@ -10,4 +10,7 @@ This source was released under the terms of Visit [CyanogenMod Github](https://github.com/CyanogenMod) and [CyanogenMod Code Review](http://review.cyanogenmod.com/) to get the source and patches. +This application uses also third party libraries. Checkout the license individual +license of every library in libs folder. + Copyright © 2012 The CyanogenMod Project diff --git a/libs/android-syntax-highlight/CHANGELOG.md b/libs/android-syntax-highlight/CHANGELOG.md new file mode 100644 index 00000000..84bfdb46 --- /dev/null +++ b/libs/android-syntax-highlight/CHANGELOG.md @@ -0,0 +1,7 @@ +ChangeLog +======================== + +Version 0.0.1 +------------- +* Initial support +* properties syntax highlight processor
\ No newline at end of file diff --git a/libs/android-syntax-highlight/LICENSE.md b/libs/android-syntax-highlight/LICENSE.md new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/libs/android-syntax-highlight/LICENSE.md @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libs/android-syntax-highlight/README.md b/libs/android-syntax-highlight/README.md new file mode 100644 index 00000000..3f7dc373 --- /dev/null +++ b/libs/android-syntax-highlight/README.md @@ -0,0 +1,14 @@ +Android Syntax Highlight Library +================================ + +A library for add syntax highlight to TextView and EditText widgets. + +This library was developed as part of the CMFileManager app. + +This source was released under the terms of +[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) license. + +Visit [CyanogenMod Github](https://github.com/CyanogenMod) and [CyanogenMod +Code Review](http://review.cyanogenmod.com/) to get the source and patches. + +Copyright © 2013 The CyanogenMod Project diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/HighlightColors.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/HighlightColors.java new file mode 100644 index 00000000..5e31dab7 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/HighlightColors.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash; + +import android.graphics.Color; + +/** + * An enumeration of all the color resources available for syntax highlight processors. + */ +public enum HighlightColors { + + /** + * Text color + */ + TEXT( + "ash_text", //$NON-NLS-1$ + "ash_text_color", //$NON-NLS-1$ + Color.argb(153, 0, 0, 0)), + /** + * Assignment text color + */ + ASSIGNMENT( + "ash_assignment", //$NON-NLS-1$ + "ash_assignment_color", //$NON-NLS-1$ + Color.argb(153, 0, 0, 0)), + /** + * Single line comment color + */ + SINGLE_LINE_COMMENT( + "ash_singleline_comment", //$NON-NLS-1$ + "ash_singleline_comment_color", //$NON-NLS-1$ + Color.argb(255, 63, 127, 95)), + /** + * Multiline line comment color + */ + MULTILINE_LINE_COMMENT( + "ash_multiline_comment", //$NON-NLS-1$ + "ash_multiline_comment_color", //$NON-NLS-1$ + Color.argb(255, 127, 159, 191)), + /** + * Keyword color + */ + KEYWORD( + "ash_keyword", //$NON-NLS-1$ + "ash_keyword_color", //$NON-NLS-1$ + Color.argb(255, 127, 0, 85)), + /** + * Quoted string color + */ + QUOTED_STRING( + "ash_quoted_string", //$NON-NLS-1$ + "ash_quoted_string_color", //$NON-NLS-1$ + Color.argb(255, 42, 0, 255)), + /** + * Variable color + */ + VARIABLE( + "ash_variable", //$NON-NLS-1$ + "ash_variable_color", //$NON-NLS-1$ + Color.argb(153, 0, 0, 192)); + + + private final String mId; + private final String mResId; + private final int mDefault; + + /** + * Constructor of <code>HighlightColors</code> + * + * @param id The id of the object + * @param resid The resource id + * @param def The default value + */ + HighlightColors(String id, String resid, int def) { + this.mId = id; + this.mResId = resid; + this.mDefault = def; + } + + /** + * Returns the identifier + * + * @return String The identifier + */ + public String getId() { + return this.mId; + } + + /** + * Returns the resource identifier + * + * @return String The resource identifier + */ + public String getResId() { + return this.mResId; + } + + /** + * Returns the default value + * + * @return String The default value + */ + public int getDefault() { + return this.mDefault; + } + +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/ISyntaxHighlightResourcesResolver.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/ISyntaxHighlightResourcesResolver.java new file mode 100644 index 00000000..11170e7c --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/ISyntaxHighlightResourcesResolver.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash; + +/** + * An interface that should be implemented by the library caller, to + * resolve resources needed by the syntax processors. + * + * @see HighlightColors + */ +public interface ISyntaxHighlightResourcesResolver { + + /** + * Method that returns a string + * + * @param id The color unique id + * @param resid The resource identifier + * @return CharSequence The string + */ + CharSequence getString(String id, String resid); + + /** + * Method that returns an integer + * + * @param id The color unique id + * @param resid The resource identifier + * @param def The default value + * @return int The integer value + */ + int getInteger(String id, String resid, int def); + + /** + * Method that returns a color + * + * @param id The color unique id + * @param resid The resource identifier + * @param def The default value + * @return int The color + */ + int getColor(String id, String resid, int def); +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/RegExpUtil.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/RegExpUtil.java new file mode 100644 index 00000000..283eba90 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/RegExpUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * A helper class for deal with patters + */ +public final class RegExpUtil { + + /** + * A constant that is returned when the no expression matches. + */ + public static final int NO_MATCH = -1; + + /** + * New line pattern + */ + public static final Pattern NEWLINE_PATTERN = Pattern.compile("(\r\n|\n|\r)"); //$NON-NLS-1$ + + /** + * Method that returns the last match position of a regexp, + * + * @param pattern The patter + * @param input The input + * @param withPattern Whether the return position should contains the pattern or not. + * @return int The matched position or -1 + */ + public static int getLastMatch(Pattern pattern, CharSequence input, boolean withPattern) { + Matcher m = pattern.matcher(input); + int p = NO_MATCH; + while (m.find()) { + p = withPattern ? m.start() : m.end(); + } + return p; + } + + /** + * Method that returns the next match position of a regexp, + * + * @param pattern The patter + * @param input The input + * @param withPattern Whether the return position should contains the pattern or not. + * @return int The matched position or -1 + */ + public static int getNextMatch(Pattern pattern, CharSequence input, boolean withPattern) { + Matcher m = pattern.matcher(input); + int p = NO_MATCH; + if (m.find()) { + return withPattern ? m.end() : m.start(); + } + return p; + } +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightFactory.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightFactory.java new file mode 100644 index 00000000..20c85dc2 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightFactory.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash; + +import com.cyanogenmod.filemanager.ash.spi.PropertiesSyntaxHighlightProcessor; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A factory of <code>SyntaxHighlightProcessor</code> classes. + */ +public class SyntaxHighlightFactory { + + private static SyntaxHighlightFactory sFactory; + + private final ArrayList<SyntaxHighlightProcessor> mProcessors; + + /** + * Constructor of <code>SyntaxHighlightFactory</code> + */ + public SyntaxHighlightFactory() { + super(); + this.mProcessors = new ArrayList<SyntaxHighlightProcessor>(); + } + + /** + * Method that returns the default highlight factory instance + * + * @param resolver A class for allow the processor to obtain resources + * @return SyntaxHighlightFactory The default syntax highlight factory + */ + public static final synchronized SyntaxHighlightFactory getDefaultFactory( + ISyntaxHighlightResourcesResolver resolver) { + if (sFactory == null) { + sFactory = createDefaultFactory(resolver); + } + return sFactory; + } + + /** + * Method that returns the syntax highlight processor that can handle the file + * + * @param file The file to process + * @return SyntaxHighlightProcessor The syntax highlight processor + */ + public SyntaxHighlightProcessor getSyntaxHighlightProcessor(File file) { + int cc = this.mProcessors.size(); + for (int i = 0; i < cc; i++) { + SyntaxHighlightProcessor processor = this.mProcessors.get(i); + if (processor.accept(file)) { + return processor; + } + } + return null; + } + + /** + * Method that return all the available syntax highlight processors. + * + * @return List<SyntaxHighlightProcessor> the list available syntax highlight processors. + */ + public List<SyntaxHighlightProcessor> getAvailableSyntaxHighlightProcessors() { + return new ArrayList<SyntaxHighlightProcessor>(this.mProcessors); + } + + /** + * Method that create the default syntax highlight factory. + * + * @param resolver A class for allow the processor to obtain resources + * @return SyntaxHighlightFactory The default factory + */ + private static SyntaxHighlightFactory createDefaultFactory( + ISyntaxHighlightResourcesResolver resolver) { + // TODO Read all processors classes of the SPI package + // For now we add all known syntax highlight processors + SyntaxHighlightFactory factory = new SyntaxHighlightFactory(); + factory.mProcessors.add(new PropertiesSyntaxHighlightProcessor(resolver)); + return factory; + } +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightProcessor.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightProcessor.java new file mode 100644 index 00000000..2d520346 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/SyntaxHighlightProcessor.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash; + +import android.text.Spannable; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; + +import java.io.File; + +/** + * The base class for all the syntax highlight processors.</br> + */ +public abstract class SyntaxHighlightProcessor { + + protected final ISyntaxHighlightResourcesResolver mResourcesResolver; + + /** + * Constructor of <code>SyntaxHighlightProcessor</code> + * + * @param resolver A class for resolve resources + */ + public SyntaxHighlightProcessor(ISyntaxHighlightResourcesResolver resolver) { + super(); + this.mResourcesResolver = resolver; + } + + /** + * Method that request to the syntax highlight processor if it is able to parse + * the file + * + * @param file The file to check + * @return boolean If the syntax highlight processor accepts process the file + */ + protected abstract boolean accept(File file); + + /** + * Method that initializes the processor + */ + public abstract void initialize(); + + /** + * Method that request to the syntax highlight processor to do process and highlight a + * document. This method request a full process. + * + * @param spanable The spannable source to highlight + */ + public abstract void process(Spannable spanable); + + /** + * Method that request to the syntax highlight processor to process and highlight a + * document. This method request a partial process. + * + * @param spanable The spannable source to highlight + * @param start The start of spannable to process + * @param end The end of spannable to process + */ + public abstract void process(Spannable spanable, int start, int end); + + /** + * Method that cancels the active processor + */ + public abstract void cancel(); + + /** + * Method that clear all the existent spans + * + * @param spanable The spannable + */ + @SuppressWarnings("static-method") + public void clear(Spannable spanable) { + ForegroundColorSpan[] spans = + spanable.getSpans(0, spanable.length(), ForegroundColorSpan.class); + int cc = spans.length; + for (int i = 0; i < cc; i++) { + spanable.removeSpan(spans[i]); + } + } + + + /** + * Method that sets a new <code>Spannable</code>. + * + * @param spanable The spannable + * @param color The color of the span + * @param start The start of the span + * @param end The end of the span + */ + @SuppressWarnings("static-method") + protected void setSpan(Spannable spanable, int color, int start, int end) { + if (start == end) return; + spanable.setSpan( + new ForegroundColorSpan(color), + start, + end, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/NewLineScanner.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/NewLineScanner.java new file mode 100644 index 00000000..5552b8eb --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/NewLineScanner.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash.scanners; + +import java.util.regex.Matcher; + +import com.cyanogenmod.filemanager.ash.RegExpUtil; + +/** + * An scanner to process an input, reporting every text into new lines. + */ +public class NewLineScanner extends Scanner { + + private final NewLineScannerListener mListener; + + /** + * The listener for the newline scanner + */ + public interface NewLineScannerListener { + /** + * When a new line is ready + * + * @param newline The newline detected + * @param start The start position of the new line within the input text + * @param end The end position of the new line within the input text + * @param sep The line separator detected + * @return boolean If processor must continue with the next line + */ + boolean onNewLine(CharSequence newline, int start, int end, CharSequence sep); + } + + /** + * Constructor of <code>Scanner</code> + * + * @param input The input + * @param listener The listener where return every new line + */ + public NewLineScanner(CharSequence input, NewLineScannerListener listener) { + super(input); + this.mListener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + public void scan() { + if (this.mInput.length() == 0) return; + Matcher m = RegExpUtil.NEWLINE_PATTERN.matcher(this.mInput); + int next = 0; + while(m.find(next)) { + CharSequence line = this.mInput.subSequence(next, m.start()); + if (!this.mListener.onNewLine(line, next, m.start(), m.group())) { + return; + } + next = m.end(); + } + // The non-matched data + CharSequence line = this.mInput.subSequence(next, this.mInput.length()); + this.mListener.onNewLine(line, next, this.mInput.length(), null); + } +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/Scanner.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/Scanner.java new file mode 100644 index 00000000..31583456 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/scanners/Scanner.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.cyanogenmod.filemanager.ash.scanners; + + +/** + * The base class for all the scanners + */ +public abstract class Scanner { + + CharSequence mInput; + + /** + * Constructor of <code>Scanner</code> + * + * @param input The input + */ + public Scanner(CharSequence input) { + super(); + this.mInput = input; + } + + /** + * Method that starts the scan process + */ + public abstract void scan(); +} diff --git a/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/spi/PropertiesSyntaxHighlightProcessor.java b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/spi/PropertiesSyntaxHighlightProcessor.java new file mode 100644 index 00000000..f9fd19a2 --- /dev/null +++ b/libs/android-syntax-highlight/src/com/cyanogenmod/filemanager/ash/spi/PropertiesSyntaxHighlightProcessor.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ash.spi; + +import android.text.Spannable; +import android.text.style.ForegroundColorSpan; + +import com.cyanogenmod.filemanager.ash.HighlightColors; +import com.cyanogenmod.filemanager.ash.ISyntaxHighlightResourcesResolver; +import com.cyanogenmod.filemanager.ash.RegExpUtil; +import com.cyanogenmod.filemanager.ash.SyntaxHighlightProcessor; +import com.cyanogenmod.filemanager.ash.scanners.NewLineScanner; +import com.cyanogenmod.filemanager.ash.scanners.NewLineScanner.NewLineScannerListener; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A <b>properties</b> highlight processor class.</br> + * </br> + * The behaviour of this class is:</br> + * <ul> + * <li>Comments start with # (only spaces are allowed prior to comment)</li> + * <li>Assignment character (=) separates key from value</li> + * <li>Arguments exists only in values, and are composed by {a digit}</li> + * <li>Values can be extended in multiple lines if line ends with the char "\". A + * comment in multiline breaks the multiline and starts a new property.</li> + * </ul> + * </br> + * IMP! This class is not thread safe. Calling "process" methods should be + * done in a synchronous way. + */ +public class PropertiesSyntaxHighlightProcessor extends SyntaxHighlightProcessor { + + private static final String EXT_PROP = "prop"; //$NON-NLS-1$ + private static final String EXT_PROPERTIES = "properties"; //$NON-NLS-1$ + + private static final Pattern COMMENT = Pattern.compile("^\\s*#.*"); //$NON-NLS-1$ + private static final Pattern MULTILINE = Pattern.compile(".*\\\\\\s*$"); //$NON-NLS-1$ + private static final Pattern ASSIGNMENT = Pattern.compile("="); //$NON-NLS-1$ + private static final Pattern ARGUMENT = Pattern.compile("\\{\\d+\\}"); //$NON-NLS-1$ + + protected Spannable mSpannable; + private boolean mMultiLine; + + private int mKeyColor; + private int mAssignmentColor; + private int mCommentColor; + private int mValueColor; + private int mArgumentColor; + + /** + * Constructor of <code>PropertiesSyntaxHighlightProcessor</code> + * + * @param resolver A class for resolve resources + */ + public PropertiesSyntaxHighlightProcessor(ISyntaxHighlightResourcesResolver resolver) { + super(resolver); + initialize(); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean accept(File file) { + if (file == null) return false; + return file.getName().toLowerCase().endsWith(EXT_PROP) || + file.getName().toLowerCase().endsWith(EXT_PROPERTIES); + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize() { + this.mMultiLine = false; + this.mSpannable = null; + if (this.mResourcesResolver != null) { + this.mKeyColor = this.mResourcesResolver.getColor( + HighlightColors.TEXT.getId(), + HighlightColors.TEXT.getResId(), + HighlightColors.TEXT.getDefault()); + this.mAssignmentColor = this.mResourcesResolver.getColor( + HighlightColors.ASSIGNMENT.getId(), + HighlightColors.ASSIGNMENT.getResId(), + HighlightColors.ASSIGNMENT.getDefault()); + this.mCommentColor = this.mResourcesResolver.getColor( + HighlightColors.SINGLE_LINE_COMMENT.getId(), + HighlightColors.SINGLE_LINE_COMMENT.getResId(), + HighlightColors.SINGLE_LINE_COMMENT.getDefault()); + this.mValueColor = this.mResourcesResolver.getColor( + HighlightColors.VARIABLE.getId(), + HighlightColors.VARIABLE.getResId(), + HighlightColors.VARIABLE.getDefault()); + this.mArgumentColor = this.mResourcesResolver.getColor( + HighlightColors.KEYWORD.getId(), + HighlightColors.KEYWORD.getResId(), + HighlightColors.KEYWORD.getDefault()); + } else { + // By default + this.mKeyColor = HighlightColors.TEXT.getDefault(); + this.mAssignmentColor = HighlightColors.TEXT.getDefault(); + this.mCommentColor = HighlightColors.SINGLE_LINE_COMMENT.getDefault(); + this.mValueColor = HighlightColors.VARIABLE.getDefault(); + this.mArgumentColor = HighlightColors.KEYWORD.getDefault(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void process(final Spannable spanable) { + this.mMultiLine = false; + this.mSpannable = spanable; + clear(spanable); + NewLineScanner scanner = new NewLineScanner(spanable, new NewLineScannerListener() { + @Override + public boolean onNewLine(CharSequence newline, int start, int end, CharSequence sep) { + processNewLine(newline, start, end); + return true; + } + + }); + scanner.scan(); + this.mSpannable = null; + } + + /** + * {@inheritDoc} + */ + @Override + public void process(final Spannable spanable, final int start, final int end) { + // We need a Retrieve the previous line + this.mMultiLine = false; + this.mSpannable = spanable; + CharSequence seqs = spanable.subSequence(0, start); + CharSequence seqe = spanable.subSequence(end, spanable.length()); + int s1 = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqs, false); + if (s1 == RegExpUtil.NO_MATCH) { + s1 = 0; + } + int e1 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, false); + if (e1 == RegExpUtil.NO_MATCH) { + e1 = spanable.length(); + } else { + e1 += end; + } + + // Also, we need to know about if the previous line is multiline + if (s1 > 0) { + int s2 = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqs, true); + CharSequence seqnl = spanable.subSequence(0, s2); + int snl = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqnl, false); + Matcher mlm = MULTILINE.matcher( + spanable.subSequence(snl != RegExpUtil.NO_MATCH ? snl : 0, s2)); + this.mMultiLine = mlm.matches(); + } + + // Process the new line + if (s1 != e1) { + processNewLine(spanable.subSequence(s1, e1), s1, e1); + } + + // Now, multiline again (next line). We check always the next line, because we + // don't know if user delete multiline flag in the current line + e1 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, true); + if (e1 != RegExpUtil.NO_MATCH) { + e1 += end; + seqe = spanable.subSequence(e1, spanable.length()); + int e2 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, false); + if (e2 == RegExpUtil.NO_MATCH) { + e2 = spanable.length(); + } else { + e2 += e1; + } + processNewLine(spanable.subSequence(e1, e2), e1, e2); + } + + this.mSpannable = null; + } + + /** + * {@inheritDoc} + */ + @Override + public void cancel() { + // Not needed by this processor + } + + /** + * A method to process every new line + * + * @param newline The newline + * @param start The start position of the line + * @param end The end position of the line + * @hide + */ + void processNewLine(CharSequence newline, int start, int end) { + // Remove all spannable of the line (this processor doesn't multiline spans and + // only uses ForegroundColorSpan spans) + ForegroundColorSpan[] spans = + this.mSpannable.getSpans(start, end, ForegroundColorSpan.class); + int cc = spans.length; + for (int i = 0; i < cc; i++) { + this.mSpannable.removeSpan(spans[i]); + } + + // Find comment + Matcher cm = COMMENT.matcher(newline); + if (cm.matches()) { + // All the line is a comment + setSpan(this.mSpannable, this.mCommentColor, start, end); + this.mMultiLine = false; + return; + } + + // Has multiline + Matcher mlm = MULTILINE.matcher(newline); + boolean ml = mlm.matches(); + + //Find the assignment + int k = this.mMultiLine ? -1 : start; + int v = start; + int v2 = 0; + int a = -1; + if (!this.mMultiLine) { + Matcher am = ASSIGNMENT.matcher(newline); + if (am.find()) { + // Assignment found + v2 = am.start() + 1; + a = start + am.start(); + v = a + 1; + } + } + + // All the string is a key + if (!this.mMultiLine && a == -1) { + setSpan(this.mSpannable, this.mKeyColor, start, end); + + } else { + // Key + if (!this.mMultiLine) { + setSpan(this.mSpannable, this.mKeyColor, k, a); + } + // Assignment + if (!this.mMultiLine) { + setSpan(this.mSpannable, this.mAssignmentColor, a, a + 1); + } + // Value + setSpan(this.mSpannable, this.mValueColor, v, end); + // Argument + Matcher argm = ARGUMENT.matcher(newline); + while (argm.find(v2)) { + int s = start + argm.start(); + int e = start + argm.end(); + setSpan(this.mSpannable, this.mArgumentColor, s, e); + v2 = argm.end(); + } + } + + // Multiline? + this.mMultiLine = ml; + } +} diff --git a/libs/color-picker-view/CHANGELOG.md b/libs/color-picker-view/CHANGELOG.md new file mode 100644 index 00000000..342ba60e --- /dev/null +++ b/libs/color-picker-view/CHANGELOG.md @@ -0,0 +1,4 @@ +ChangeLog +======================== + +The source was grabbed from version 1.0 (r8) diff --git a/libs/color-picker-view/LICENSE.md b/libs/color-picker-view/LICENSE.md new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/libs/color-picker-view/LICENSE.md @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libs/color-picker-view/README.md b/libs/color-picker-view/README.md new file mode 100644 index 00000000..b26ef5aa --- /dev/null +++ b/libs/color-picker-view/README.md @@ -0,0 +1,18 @@ +mColorPicker +================================ + +A color picker is something that has always been missing from the standard +set of components which developers can build their user interface in Android +with. Although there have been a few color pickers floating around on the +internet I never found any that I thought was good enough for use in my +applications so I sat down to write my own. This is the result and I have +decided to release it as open source application for all you developers +out there to use, free of charge of course. + +Checkout latest sources at http://code.google.com/p/color-picker-view/ + +This library is released under the [Apache 2.0] +http://www.apache.org/licenses/LICENSE-2.0.html) license. + +Copyright © 2010 Daniel Nilsson +Copyright © 2013 The CyanogenMod Project diff --git a/libs/color-picker-view/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java b/libs/color-picker-view/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java new file mode 100644 index 00000000..e878756d --- /dev/null +++ b/libs/color-picker-view/src/afzkl/development/mColorPicker/drawables/AlphaPatternDrawable.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package afzkl.development.mColorPicker.drawables; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +/** + * This drawable that draws a simple white and gray chessboard pattern. + * It's pattern you will often see as a background behind a + * partly transparent image in many applications. + * @author Daniel Nilsson + */ +@SuppressWarnings("all") +public class AlphaPatternDrawable extends Drawable { + + private int mRectangleSize = 10; + + private final Paint mPaint = new Paint(); + private final Paint mPaintWhite = new Paint(); + private final Paint mPaintGray = new Paint(); + + private int numRectanglesHorizontal; + private int numRectanglesVertical; + + /** + * Bitmap in which the pattern will be cahched. + */ + private Bitmap mBitmap; + + public AlphaPatternDrawable(int rectangleSize) { + mRectangleSize = rectangleSize; + mPaintWhite.setColor(0xffffffff); + mPaintGray.setColor(0xffcbcbcb); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawBitmap(mBitmap, null, getBounds(), mPaint); + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public void setAlpha(int alpha) { + throw new UnsupportedOperationException("Alpha is not supported by this drawwable."); + } + + @Override + public void setColorFilter(ColorFilter cf) { + throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable."); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + + int height = bounds.height(); + int width = bounds.width(); + + numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize)); + numRectanglesVertical = (int) Math.ceil(height / mRectangleSize); + + generatePatternBitmap(); + + } + + /** + * This will generate a bitmap with the pattern + * as big as the rectangle we were allow to draw on. + * We do this to chache the bitmap so we don't need to + * recreate it each time draw() is called since it + * takes a few milliseconds. + */ + private void generatePatternBitmap() { + + if (getBounds().width() <= 0 || getBounds().height() <= 0) { + return; + } + + mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888); + Canvas canvas = new Canvas(mBitmap); + + Rect r = new Rect(); + boolean verticalStartWhite = true; + for (int i = 0; i <= numRectanglesVertical; i++) { + + boolean isWhite = verticalStartWhite; + for (int j = 0; j <= numRectanglesHorizontal; j++) { + + r.top = i * mRectangleSize; + r.left = j * mRectangleSize; + r.bottom = r.top + mRectangleSize; + r.right = r.left + mRectangleSize; + + canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray); + + isWhite = !isWhite; + } + + verticalStartWhite = !verticalStartWhite; + + } + + } + +} diff --git a/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorDialogView.java b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorDialogView.java new file mode 100644 index 00000000..d35becf2 --- /dev/null +++ b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorDialogView.java @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2013 Jorge Ruesga + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package afzkl.development.mColorPicker.views; + +import afzkl.development.mColorPicker.views.ColorPickerView.OnColorChangedListener; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.Spanned; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * A view use directly into a dialog. It contains a one {@link ColorPickerView} + * and two {@link ColorPanelView} (the current color and the new color) + */ +public class ColorDialogView extends RelativeLayout + implements OnColorChangedListener, TextWatcher { + + private static final int DEFAULT_MARGIN_DP = 16; + private static final int DEFAULT_PANEL_HEIGHT_DP = 32; + private static final int DEFAULT_TEXT_SIZE_SP = 12; + private static final int DEFAULT_LABEL_TEXT_SIZE_SP = 18; + + private ColorPickerView mPickerView; + private ColorPanelView mCurrentColorView; + private ColorPanelView mNewColorView; + private TextView tvCurrent; + private TextView tvNew; + private TextView tvColorLabel; + private EditText etColor; + + private String mCurrentLabelText = "Current:"; //$NON-NLS-1$ + private String mNewLabelText = "New:"; //$NON-NLS-1$ + + private String mColorLabelText = "Color:"; //$NON-NLS-1$ + + /** + * Constructor of <code>ColorDialogView</code> + * + * @param context The current context + */ + public ColorDialogView(Context context) { + this(context, null); + } + + /** + * Constructor of <code>ColorDialogView</code> + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public ColorDialogView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructor of <code>ColorDialogView</code> + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public ColorDialogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the view. This method loads all the necessary + * information and create an appropriate layout for the view + */ + private void init() { + // To fight color branding. + ((Activity)getContext()).getWindow().setFormat(PixelFormat.RGBA_8888); + + // Creates the color input field + int id = createColorInput(); + + // Creates the color picker + id = createColorPicker(id); + + // Creates the current color and new color panels + id = createColorsPanel(id); + + // Sets the input color + this.etColor.setText(toHex(this.mNewColorView.getColor())); + } + + /** + * Method that creates the color input + */ + private int createColorInput() { + final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP); + LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + android.view.ViewGroup.LayoutParams.MATCH_PARENT); + lp2.setMargins(0, 0, dlgMarging, 0); + this.tvColorLabel = new TextView(getContext()); + this.tvColorLabel.setText(this.mColorLabelText); + this.tvColorLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_LABEL_TEXT_SIZE_SP); + this.tvColorLabel.setGravity(Gravity.BOTTOM | Gravity.LEFT); + this.tvColorLabel.setLayoutParams(lp2); + + lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + this.etColor = new EditText(getContext()); + this.etColor.setSingleLine(); + this.etColor.setGravity(Gravity.TOP | Gravity.LEFT); + this.etColor.setCursorVisible(true); + this.etColor.setImeOptions( + EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN); + this.etColor.setInputType( + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + this.etColor.setLayoutParams(lp2); + InputFilter[] filters = new InputFilter[2]; + filters[0] = new InputFilter.LengthFilter(8); + filters[1] = new InputFilter() { + @Override + public CharSequence filter(CharSequence source, int start, + int end, Spanned dest, int dstart, int dend) { + if (start >= end) return ""; //$NON-NLS-1$ + String s = source.subSequence(start, end).toString(); + StringBuilder sb = new StringBuilder(); + int cc = s.length(); + for (int i = 0; i < cc; i++) { + char c = s.charAt(i); + if ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) { + sb.append(c); + } + } + return sb.toString().toUpperCase(); + } + }; + this.etColor.setFilters(filters); + this.etColor.addTextChangedListener(this); + + LinearLayout ll1 = new LinearLayout(getContext()); + ll1.setId(generateViewId()); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + lp.setMargins(dlgMarging, 0, dlgMarging, 0); + lp.addRule(RelativeLayout.ALIGN_PARENT_TOP); + ll1.setLayoutParams(lp); + ll1.addView(this.tvColorLabel); + ll1.addView(this.etColor); + addView(ll1); + + return ll1.getId(); + } + + /** + * Method that creates the color picker + * + * @param belowOf The anchor view + * @return id The layout id + */ + private int createColorPicker(int belowOf) { + final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP); + this.mPickerView = new ColorPickerView(getContext()); + this.mPickerView.setId(generateViewId()); + this.mPickerView.setOnColorChangedListener(this); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + lp.setMargins(dlgMarging, 0, dlgMarging, 0); + lp.addRule(RelativeLayout.BELOW, belowOf); + this.mPickerView.setLayoutParams(lp); + addView(this.mPickerView); + return this.mPickerView.getId(); + } + + /** + * Method that creates the colors panel (current and new) + * + * @param belowOf The anchor view + * @return id The layout id + */ + private int createColorsPanel(int belowOf) { + final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP); + final int panelHeight = (int)convertDpToPixel(DEFAULT_PANEL_HEIGHT_DP); + LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + 1); + + // Titles + this.tvCurrent = new TextView(getContext()); + lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + 1); + this.tvCurrent.setLayoutParams(lp2); + this.tvCurrent.setText(this.mCurrentLabelText); + this.tvCurrent.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP); + this.tvNew = new TextView(getContext()); + this.tvNew.setLayoutParams(lp2); + this.tvNew.setText(this.mNewLabelText); + this.tvNew.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP); + TextView sep1 = new TextView(getContext()); + lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + 0); + lp2.setMargins(dlgMarging, 0, dlgMarging, 0); + sep1.setLayoutParams(lp2); + sep1.setText(" "); //$NON-NLS-1$ + sep1.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP); + + LinearLayout ll1 = new LinearLayout(getContext()); + ll1.setId(generateViewId()); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2); + lp.addRule(RelativeLayout.BELOW, belowOf); + ll1.setLayoutParams(lp); + ll1.addView(this.tvCurrent); + ll1.addView(sep1); + ll1.addView(this.tvNew); + addView(ll1); + + // Color panels + lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + 1); + this.mCurrentColorView = new ColorPanelView(getContext()); + this.mCurrentColorView.setLayoutParams(lp2); + this.mNewColorView = new ColorPanelView(getContext()); + this.mNewColorView.setLayoutParams(lp2); + TextView sep2 = new TextView(getContext()); + lp2 = new LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + 0); + lp2.setMargins(dlgMarging, 0, dlgMarging, 0); + sep2.setLayoutParams(lp2); + sep2.setText("-"); //$NON-NLS-1$ + sep2.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP); + + LinearLayout ll2 = new LinearLayout(getContext()); + ll2.setId(generateViewId()); + lp = new RelativeLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, panelHeight); + lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2); + lp.addRule(RelativeLayout.BELOW, ll1.getId()); + ll2.setLayoutParams(lp); + ll2.addView(this.mCurrentColorView); + ll2.addView(sep2); + ll2.addView(this.mNewColorView); + addView(ll2); + + return ll2.getId(); + } + + /** + * Method that returns the color of the picker + * + * @return The ARGB color + */ + public int getColor() { + return this.mPickerView.getColor(); + } + + /** + * Method that set the color of the picker + * + * @param argb The ARGB color + */ + public void setColor(int argb) { + setColor(argb, false); + } + + /** + * Method that set the color of the picker + * + * @param argb The ARGB color + * @param fromEditText If the call comes from the <code>EditText</code> + */ + private void setColor(int argb, boolean fromEditText) { + this.mPickerView.setColor(argb, false); + this.mCurrentColorView.setColor(argb); + this.mNewColorView.setColor(argb); + if (!fromEditText) { + this.etColor.setText(toHex(this.mNewColorView.getColor())); + } + } + + /** + * Method that display/hide the alpha slider + * + * @param show If the alpha slider should be shown + */ + public void showAlphaSlider(boolean show) { + this.mPickerView.setAlphaSliderVisible(show); + } + + /** + * Set the text that should be shown in the alpha slider. + * Set to null to disable text. + * + * @param text Text that should be shown. + */ + public void setAlphaSliderText(String text) { + this.mPickerView.setAlphaSliderText(text); + } + + /** + * Set the text that should be shown in the actual color panel. + * Set to null to disable text. + * + * @param text Text that should be shown. + */ + public void setCurrentColorText(String text) { + this.mCurrentLabelText = text; + this.tvCurrent.setText(this.mCurrentLabelText); + } + + /** + * Set the text that should be shown in the new color panel. + * Set to null to disable text. + * + * @param text Text that should be shown. + */ + public void setNewColorText(String text) { + this.mNewLabelText = text; + this.tvNew.setText(this.mNewLabelText); + } + + /** + * Set the text that should be shown in the label of the color input. + * Set to null to disable text. + * + * @param text Text that should be shown. + */ + public void setColorLabelText(String text) { + this.mColorLabelText = text; + this.tvColorLabel.setText(this.mColorLabelText); + } + + /** + * {@inheritDoc} + */ + @Override + public void onColorChanged(int color) { + this.mNewColorView.setColor(color); + this.etColor.removeTextChangedListener(this); + this.etColor.setText(toHex(this.mNewColorView.getColor())); + this.etColor.addTextChangedListener(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {/**NON BLOCK**/} + + /** + * {@inheritDoc} + */ + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {/**NON BLOCK**/} + + /** + * {@inheritDoc} + */ + @Override + public void afterTextChanged(Editable s) { + if (s.length() == 8) { + try { + setColor(toARGB(s.toString()), true); + } catch (Exception e) {/**NON BLOCK**/} + } + } + + /** + * This method converts dp unit to equivalent device specific value in pixels. + * + * @param ctx The current context + * @param dp A value in dp (Device independent pixels) unit + * @return float A float value to represent Pixels equivalent to dp according to device + */ + private float convertDpToPixel(float dp) { + Resources resources = getContext().getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return dp * (metrics.densityDpi / 160f); + } + + /** + * Method that converts an ARGB color to its hex string color representation + * + * @param argb The ARGB color + * @return String The hex string representation of the color + */ + private static String toHex(int argb) { + StringBuilder sb = new StringBuilder(); + sb.append(toHexString((byte)Color.alpha(argb))); + sb.append(toHexString((byte)Color.red(argb))); + sb.append(toHexString((byte)Color.green(argb))); + sb.append(toHexString((byte)Color.blue(argb))); + return sb.toString(); + } + + /** + * Method that converts an hex string color representation to an ARGB color + * + * @param hex The hex string representation of the color + * @return int The ARGB color + */ + private static int toARGB(String hex) { + return Color.parseColor("#" + hex); //$NON-NLS-1$ + } + + /** + * Method that converts a byte into its hex string representation + * + * @param v The value to convert + * @return String The hex string representation + */ + private static String toHexString(byte v) { + String hex = Integer.toHexString(v & 0xff); + if (hex.length() == 1) { + hex = "0" + hex; //$NON-NLS-1$ + } + return hex.toUpperCase(); + } +} diff --git a/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPanelView.java b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPanelView.java new file mode 100644 index 00000000..9764ff1d --- /dev/null +++ b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPanelView.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package afzkl.development.mColorPicker.views; + +import afzkl.development.mColorPicker.drawables.AlphaPatternDrawable; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +/** + * This class draws a panel which which will be filled with a color which can be set. + * It can be used to show the currently selected color which you will get from + * the {@link ColorPickerView}. + * @author Daniel Nilsson + * + */ +@SuppressWarnings("all") +public class ColorPanelView extends View{ + + /** + * The width in pixels of the border + * surrounding the color panel. + */ + private final static float BORDER_WIDTH_PX = 1; + + private static float mDensity = 1f; + + private int mBorderColor = 0xff6E6E6E; + private int mColor = 0xff000000; + + private Paint mBorderPaint; + private Paint mColorPaint; + + private RectF mDrawingRect; + private RectF mColorRect; + + private AlphaPatternDrawable mAlphaPattern; + + + public ColorPanelView(Context context) { + this(context, null); + } + + public ColorPanelView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ColorPanelView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + init(); + } + + private void init() { + mBorderPaint = new Paint(); + mColorPaint = new Paint(); + mDensity = getContext().getResources().getDisplayMetrics().density; + } + + + @Override + protected void onDraw(Canvas canvas) { + + final RectF rect = mColorRect; + + if (BORDER_WIDTH_PX > 0) { + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(mDrawingRect, mBorderPaint); + } + + if (mAlphaPattern != null) { + mAlphaPattern.draw(canvas); + } + + mColorPaint.setColor(mColor); + + canvas.drawRect(rect, mColorPaint); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + setMeasuredDimension(width, height); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mDrawingRect = new RectF(); + mDrawingRect.left = getPaddingLeft(); + mDrawingRect.right = w - getPaddingRight(); + mDrawingRect.top = getPaddingTop(); + mDrawingRect.bottom = h - getPaddingBottom(); + + setUpColorRect(); + + } + + private void setUpColorRect() { + final RectF dRect = mDrawingRect; + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX; + float right = dRect.right - BORDER_WIDTH_PX; + + mColorRect = new RectF(left,top, right, bottom); + + mAlphaPattern = new AlphaPatternDrawable((int)(5 * mDensity)); + + mAlphaPattern.setBounds(Math.round(mColorRect.left), + Math.round(mColorRect.top), + Math.round(mColorRect.right), + Math.round(mColorRect.bottom)); + + } + + /** + * Set the color that should be shown by this view. + * @param color + */ + public void setColor(int color) { + mColor = color; + invalidate(); + } + + /** + * Get the color currently show by this view. + * @return + */ + public int getColor() { + return mColor; + } + + /** + * Set the color of the border surrounding the panel. + * @param color + */ + public void setBorderColor(int color) { + mBorderColor = color; + invalidate(); + } + + /** + * Get the color of the border surrounding the panel. + */ + public int getBorderColor() { + return mBorderColor; + } + +} diff --git a/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPickerView.java b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPickerView.java new file mode 100644 index 00000000..eb216c88 --- /dev/null +++ b/libs/color-picker-view/src/afzkl/development/mColorPicker/views/ColorPickerView.java @@ -0,0 +1,998 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package afzkl.development.mColorPicker.views; + +import afzkl.development.mColorPicker.drawables.AlphaPatternDrawable; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ComposeShader; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.Shader.TileMode; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import java.lang.reflect.Method; + +/** + * Displays a color picker to the user and allow them + * to select a color. A slider for the alpha channel is + * also available. Enable it by setting + * setAlphaSliderVisible(boolean) to true. + * @author Daniel Nilsson + */ +@SuppressWarnings("all") +public class ColorPickerView extends View{ + + public interface OnColorChangedListener{ + public void onColorChanged(int color); + } + + private final static int PANEL_SAT_VAL = 0; + private final static int PANEL_HUE = 1; + private final static int PANEL_ALPHA = 2; + + /** + * The width in pixels of the border + * surrounding all color panels. + */ + private final static float BORDER_WIDTH_PX = 1; + + /** + * The width in dp of the hue panel. + */ + private float HUE_PANEL_WIDTH = 30f; + /** + * The height in dp of the alpha panel + */ + private float ALPHA_PANEL_HEIGHT = 20f; + /** + * The distance in dp between the different + * color panels. + */ + private float PANEL_SPACING = 10f; + /** + * The radius in dp of the color palette tracker circle. + */ + private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f; + /** + * The dp which the tracker of the hue or alpha panel + * will extend outside of its bounds. + */ + private float RECTANGLE_TRACKER_OFFSET = 2f; + + + private static float mDensity = 1f; + + private OnColorChangedListener mListener; + + private Paint mSatValPaint; + private Paint mSatValTrackerPaint; + + private Paint mHuePaint; + private Paint mHueTrackerPaint; + + private Paint mAlphaPaint; + private Paint mAlphaTextPaint; + + private Paint mBorderPaint; + + private Shader mValShader; + private Shader mSatShader; + private Shader mHueShader; + private Shader mAlphaShader; + + private int mAlpha = 0xff; + private float mHue = 360f; + private float mSat = 0f; + private float mVal = 0f; + + private String mAlphaSliderText = "Alpha"; + private int mSliderTrackerColor = 0xff1c1c1c; + private int mBorderColor = 0xff6E6E6E; + private boolean mShowAlphaPanel = false; + + /* + * To remember which panel that has the "focus" when + * processing hardware button data. + */ + private int mLastTouchedPanel = PANEL_SAT_VAL; + + /** + * Offset from the edge we must have or else + * the finger tracker will get clipped when + * it is drawn outside of the view. + */ + private float mDrawingOffset; + + + /* + * Distance form the edges of the view + * of where we are allowed to draw. + */ + private RectF mDrawingRect; + + private RectF mSatValRect; + private RectF mHueRect; + private RectF mAlphaRect; + + private AlphaPatternDrawable mAlphaPattern; + + private Point mStartTouchPoint = null; + + + public ColorPickerView(Context context) { + this(context, null); + } + + public ColorPickerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ColorPickerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + mDensity = getContext().getResources().getDisplayMetrics().density; + PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity; + RECTANGLE_TRACKER_OFFSET *= mDensity; + HUE_PANEL_WIDTH *= mDensity; + ALPHA_PANEL_HEIGHT *= mDensity; + PANEL_SPACING = PANEL_SPACING * mDensity; + + mDrawingOffset = calculateRequiredOffset(); + + initPaintTools(); + + //Needed for receiving trackball motion events. + setFocusable(true); + setFocusableInTouchMode(true); + } + + private void initPaintTools() { + + mSatValPaint = new Paint(); + mSatValTrackerPaint = new Paint(); + mHuePaint = new Paint(); + mHueTrackerPaint = new Paint(); + mAlphaPaint = new Paint(); + mAlphaTextPaint = new Paint(); + mBorderPaint = new Paint(); + + + mSatValTrackerPaint.setStyle(Style.STROKE); + mSatValTrackerPaint.setStrokeWidth(2f * mDensity); + mSatValTrackerPaint.setAntiAlias(true); + + mHueTrackerPaint.setColor(mSliderTrackerColor); + mHueTrackerPaint.setStyle(Style.STROKE); + mHueTrackerPaint.setStrokeWidth(2f * mDensity); + mHueTrackerPaint.setAntiAlias(true); + + mAlphaTextPaint.setColor(0xff1c1c1c); + mAlphaTextPaint.setTextSize(14f * mDensity); + mAlphaTextPaint.setAntiAlias(true); + mAlphaTextPaint.setTextAlign(Align.CENTER); + mAlphaTextPaint.setFakeBoldText(true); + + + } + + private float calculateRequiredOffset() { + float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET); + offset = Math.max(offset, BORDER_WIDTH_PX * mDensity); + + return offset * 1.5f; + } + + private int[] buildHueColorArray() { + + int[] hue = new int[361]; + + int count = 0; + for (int i = hue.length -1; i >= 0; i--, count++) { + hue[count] = Color.HSVToColor(new float[]{i, 1f, 1f}); + } + + return hue; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + checkHardwareAccelerationSupport(); + } + + @Override + protected void onDraw(Canvas canvas) { + + if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) return; + + drawSatValPanel(canvas); + drawHuePanel(canvas); + drawAlphaPanel(canvas); + + } + + private void drawSatValPanel(Canvas canvas) { + + final RectF rect = mSatValRect; + + if (BORDER_WIDTH_PX > 0) { + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, mBorderPaint); + } + + if (mValShader == null) { + mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, + 0xffffffff, 0xff000000, TileMode.CLAMP); + } + + int rgb = Color.HSVToColor(new float[]{mHue,1f,1f}); + + mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, + 0xffffffff, rgb, TileMode.CLAMP); + ComposeShader mShader = new ComposeShader(mValShader, mSatShader, PorterDuff.Mode.MULTIPLY); + mSatValPaint.setShader(mShader); + + canvas.drawRect(rect, mSatValPaint); + + Point p = satValToPoint(mSat, mVal); + + mSatValTrackerPaint.setColor(0xff000000); + canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity, mSatValTrackerPaint); + + mSatValTrackerPaint.setColor(0xffdddddd); + canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint); + + } + + private void drawHuePanel(Canvas canvas) { + + final RectF rect = mHueRect; + + if (BORDER_WIDTH_PX > 0) { + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(rect.left - BORDER_WIDTH_PX, + rect.top - BORDER_WIDTH_PX, + rect.right + BORDER_WIDTH_PX, + rect.bottom + BORDER_WIDTH_PX, + mBorderPaint); + } + + if (mHueShader == null) { + mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, buildHueColorArray(), null, TileMode.CLAMP); + mHuePaint.setShader(mHueShader); + } + + canvas.drawRect(rect, mHuePaint); + + float rectHeight = 4 * mDensity / 2; + + Point p = hueToPoint(mHue); + + RectF r = new RectF(); + r.left = rect.left - RECTANGLE_TRACKER_OFFSET; + r.right = rect.right + RECTANGLE_TRACKER_OFFSET; + r.top = p.y - rectHeight; + r.bottom = p.y + rectHeight; + + + canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); + + } + + private void drawAlphaPanel(Canvas canvas) { + + if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) return; + + final RectF rect = mAlphaRect; + + if (BORDER_WIDTH_PX > 0) { + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(rect.left - BORDER_WIDTH_PX, + rect.top - BORDER_WIDTH_PX, + rect.right + BORDER_WIDTH_PX, + rect.bottom + BORDER_WIDTH_PX, + mBorderPaint); + } + + + mAlphaPattern.draw(canvas); + + float[] hsv = new float[]{mHue,mSat,mVal}; + int color = Color.HSVToColor(hsv); + int acolor = Color.HSVToColor(0, hsv); + + mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, + color, acolor, TileMode.CLAMP); + + + mAlphaPaint.setShader(mAlphaShader); + + canvas.drawRect(rect, mAlphaPaint); + + if (mAlphaSliderText != null && mAlphaSliderText!= "") { + canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity, mAlphaTextPaint); + } + + float rectWidth = 4 * mDensity / 2; + + Point p = alphaToPoint(mAlpha); + + RectF r = new RectF(); + r.left = p.x - rectWidth; + r.right = p.x + rectWidth; + r.top = rect.top - RECTANGLE_TRACKER_OFFSET; + r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET; + + canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); + + } + + + private Point hueToPoint(float hue) { + + final RectF rect = mHueRect; + final float height = rect.height(); + + Point p = new Point(); + + p.y = (int) (height - (hue * height / 360f) + rect.top); + p.x = (int) rect.left; + + return p; + } + + private Point satValToPoint(float sat, float val) { + + final RectF rect = mSatValRect; + final float height = rect.height(); + final float width = rect.width(); + + Point p = new Point(); + + p.x = (int) (sat * width + rect.left); + p.y = (int) ((1f - val) * height + rect.top); + + return p; + } + + private Point alphaToPoint(int alpha) { + + final RectF rect = mAlphaRect; + final float width = rect.width(); + + Point p = new Point(); + + p.x = (int) (width - (alpha * width / 0xff) + rect.left); + p.y = (int) rect.top; + + return p; + + } + + private float[] pointToSatVal(float x, float y) { + + final RectF rect = mSatValRect; + float[] result = new float[2]; + + float width = rect.width(); + float height = rect.height(); + + if (x < rect.left) { + x = 0f; + } + else if (x > rect.right) { + x = width; + } + else{ + x = x - rect.left; + } + + if (y < rect.top) { + y = 0f; + } + else if (y > rect.bottom) { + y = height; + } + else{ + y = y - rect.top; + } + + + result[0] = 1.f / width * x; + result[1] = 1.f - (1.f / height * y); + + return result; + } + + private float pointToHue(float y) { + + final RectF rect = mHueRect; + + float height = rect.height(); + + if (y < rect.top) { + y = 0f; + } + else if (y > rect.bottom) { + y = height; + } + else{ + y = y - rect.top; + } + + return 360f - (y * 360f / height); + } + + private int pointToAlpha(int x) { + + final RectF rect = mAlphaRect; + final int width = (int) rect.width(); + + if (x < rect.left) { + x = 0; + } + else if (x > rect.right) { + x = width; + } + else{ + x = x - (int)rect.left; + } + + return 0xff - (x * 0xff / width); + + } + + + @Override + public boolean onTrackballEvent(MotionEvent event) { + + float x = event.getX(); + float y = event.getY(); + + boolean update = false; + + + if (event.getAction() == MotionEvent.ACTION_MOVE) { + + switch(mLastTouchedPanel) { + + case PANEL_SAT_VAL: + + float sat, val; + + sat = mSat + x/50f; + val = mVal - y/50f; + + if (sat < 0f) { + sat = 0f; + } + else if (sat > 1f) { + sat = 1f; + } + + if (val < 0f) { + val = 0f; + } + else if (val > 1f) { + val = 1f; + } + + mSat = sat; + mVal = val; + + update = true; + + break; + + case PANEL_HUE: + + float hue = mHue - y * 10f; + + if (hue < 0f) { + hue = 0f; + } + else if (hue > 360f) { + hue = 360f; + } + + mHue = hue; + + update = true; + + break; + + case PANEL_ALPHA: + + if (!mShowAlphaPanel || mAlphaRect == null) { + update = false; + } + else{ + + int alpha = (int) (mAlpha - x*10); + + if (alpha < 0) { + alpha = 0; + } + else if (alpha > 0xff) { + alpha = 0xff; + } + + mAlpha = alpha; + + + update = true; + } + + break; + } + + + } + + + if (update) { + + if (mListener != null) { + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + return true; + } + + + return super.onTrackballEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + + boolean update = false; + + switch(event.getAction()) { + + case MotionEvent.ACTION_DOWN: + + mStartTouchPoint = new Point((int)event.getX(), (int)event.getY()); + + update = moveTrackersIfNeeded(event); + + break; + + case MotionEvent.ACTION_MOVE: + + update = moveTrackersIfNeeded(event); + + break; + + case MotionEvent.ACTION_UP: + + mStartTouchPoint = null; + + update = moveTrackersIfNeeded(event); + + break; + + } + + if (update) { + + if (mListener != null) { + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + return true; + } + + + return super.onTouchEvent(event); + } + + private boolean moveTrackersIfNeeded(MotionEvent event) { + + if (mStartTouchPoint == null) return false; + + boolean update = false; + + int startX = mStartTouchPoint.x; + int startY = mStartTouchPoint.y; + + + if (mHueRect.contains(startX, startY)) { + mLastTouchedPanel = PANEL_HUE; + + mHue = pointToHue(event.getY()); + + update = true; + } + else if (mSatValRect.contains(startX, startY)) { + + mLastTouchedPanel = PANEL_SAT_VAL; + + float[] result = pointToSatVal(event.getX(), event.getY()); + + mSat = result[0]; + mVal = result[1]; + + update = true; + } + else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) { + + mLastTouchedPanel = PANEL_ALPHA; + + mAlpha = pointToAlpha((int)event.getX()); + + update = true; + } + + + return update; + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + int width = 0; + int height = 0; + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + int widthAllowed = MeasureSpec.getSize(widthMeasureSpec); + int heightAllowed = MeasureSpec.getSize(heightMeasureSpec); + + + widthAllowed = chooseWidth(widthMode, widthAllowed); + heightAllowed = chooseHeight(heightMode, heightAllowed); + + + if (!mShowAlphaPanel) { + height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH); + + //If calculated height (based on the width) is more than the allowed height. + if (height > heightAllowed) { + height = heightAllowed; + width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH); + } + else{ + width = widthAllowed; + } + } + else{ + + width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH); + + if (width > widthAllowed) { + width = widthAllowed; + height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT); + } + else{ + height = heightAllowed; + } + + + } + + + setMeasuredDimension(width, height); + } + + private int chooseWidth(int mode, int size) { + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { + return size; + } else { // (mode == MeasureSpec.UNSPECIFIED) + return getPrefferedWidth(); + } + } + + private int chooseHeight(int mode, int size) { + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { + return size; + } else { // (mode == MeasureSpec.UNSPECIFIED) + return getPreferedHeight(); + } + } + + private int getPrefferedWidth() { + + int width = getPreferedHeight(); + + if (mShowAlphaPanel) { + width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT); + } + + + return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING); + + } + + private int getPreferedHeight() { + + int height = (int)(200 * mDensity); + + if (mShowAlphaPanel) { + height += PANEL_SPACING + ALPHA_PANEL_HEIGHT; + } + + return height; + } + + + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mDrawingRect = new RectF(); + mDrawingRect.left = mDrawingOffset + getPaddingLeft(); + mDrawingRect.right = w - mDrawingOffset - getPaddingRight(); + mDrawingRect.top = mDrawingOffset + getPaddingTop(); + mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom(); + + setUpSatValRect(); + setUpHueRect(); + setUpAlphaRect(); + } + + private void setUpSatValRect() { + + final RectF dRect = mDrawingRect; + float panelSide = dRect.height() - BORDER_WIDTH_PX * 2; + + if (mShowAlphaPanel) { + panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT; + } + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = top + panelSide; + float right = left + panelSide; + + mSatValRect = new RectF(left,top, right, bottom); + } + + private void setUpHueRect() { + final RectF dRect = mDrawingRect; + + float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0); + float right = dRect.right - BORDER_WIDTH_PX; + + mHueRect = new RectF(left, top, right, bottom); + } + + private void setUpAlphaRect() { + + if (!mShowAlphaPanel) return; + + final RectF dRect = mDrawingRect; + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX; + float right = dRect.right - BORDER_WIDTH_PX; + + mAlphaRect = new RectF(left, top, right, bottom); + + + mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity)); + mAlphaPattern.setBounds(Math.round(mAlphaRect.left), Math + .round(mAlphaRect.top), Math.round(mAlphaRect.right), Math + .round(mAlphaRect.bottom)); + + + + } + + + /** + * Set a OnColorChangedListener to get notified when the color + * selected by the user has changed. + * @param listener + */ + public void setOnColorChangedListener(OnColorChangedListener listener) { + mListener = listener; + } + + /** + * Set the color of the border surrounding all panels. + * @param color + */ + public void setBorderColor(int color) { + mBorderColor = color; + invalidate(); + } + + /** + * Get the color of the border surrounding all panels. + */ + public int getBorderColor() { + return mBorderColor; + } + + /** + * Get the current color this view is showing. + * @return the current color. + */ + public int getColor() { + return Color.HSVToColor(mAlpha, new float[]{mHue,mSat,mVal}); + } + + /** + * Set the color the view should show. + * @param color The color that should be selected. + */ + public void setColor(int color) { + setColor(color, false); + } + + /** + * Set the color this view should show. + * @param color The color that should be selected. + * @param callback If you want to get a callback to + * your OnColorChangedListener. + */ + public void setColor(int color, boolean callback) { + + int alpha = Color.alpha(color); + int red = Color.red(color); + int blue = Color.blue(color); + int green = Color.green(color); + + float[] hsv = new float[3]; + + Color.RGBToHSV(red, green, blue, hsv); + + mAlpha = alpha; + mHue = hsv[0]; + mSat = hsv[1]; + mVal = hsv[2]; + + if (callback && mListener != null) { + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + } + + /** + * Get the drawing offset of the color picker view. + * The drawing offset is the distance from the side of + * a panel to the side of the view minus the padding. + * Useful if you want to have your own panel below showing + * the currently selected color and want to align it perfectly. + * @return The offset in pixels. + */ + public float getDrawingOffset() { + return mDrawingOffset; + } + + /** + * Set if the user is allowed to adjust the alpha panel. Default is false. + * If it is set to false no alpha will be set. + * @param visible + */ + public void setAlphaSliderVisible(boolean visible) { + + if (mShowAlphaPanel != visible) { + mShowAlphaPanel = visible; + + /* + * Reset all shader to force a recreation. + * Otherwise they will not look right after + * the size of the view has changed. + */ + mValShader = null; + mSatShader = null; + mHueShader = null; + mAlphaShader = null;; + + requestLayout(); + } + + } + + public void setSliderTrackerColor(int color) { + mSliderTrackerColor = color; + + mHueTrackerPaint.setColor(mSliderTrackerColor); + + invalidate(); + } + + public int getSliderTrackerColor() { + return mSliderTrackerColor; + } + + /** + * Set the text that should be shown in the + * alpha slider. Set to null to disable text. + * @param res string resource id. + */ + public void setAlphaSliderText(int res) { + String text = getContext().getString(res); + setAlphaSliderText(text); + } + + /** + * Set the text that should be shown in the + * alpha slider. Set to null to disable text. + * @param text Text that should be shown. + */ + public void setAlphaSliderText(String text) { + mAlphaSliderText = text; + invalidate(); + } + + /** + * Get the current value of the text + * that will be shown in the alpha + * slider. + * @return + */ + public String getAlphaSliderText() { + return mAlphaSliderText; + } + + /** + * Method that checks the support for HardwareAcceleration. Check AOSP notice<br/> + * <br/> + * <pre> + * 'ComposeShader can only contain shaders of different types (a BitmapShader and a + * LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your + * application is affected by any of these missing features or limitations, you can turn + * off hardware acceleration for just the affected portion of your application by calling + * setLayerType(View.LAYER_TYPE_SOFTWARE, null).' + */ + private void checkHardwareAccelerationSupport() { + // HardwareAcceleration sit is only available since ICS. 14 = ICS_VERSION_CODE + if (android.os.Build.VERSION.SDK_INT >= 14) { + try{ + // We need to use reflection to get that method to avoid compilation errors + Method isHardwareAccelerated = + getClass().getMethod("isHardwareAccelerated", new Class[]{}); + Object o = isHardwareAccelerated.invoke(this, new Object[]{}); + if (null != o && o instanceof Boolean && (Boolean)o) { + // HardwareAcceleration is supported. Use SoftwareAcceleration + Method setLayerType = + getClass().getMethod( + "setLayerType", int.class, android.graphics.Paint.class); + setLayerType.invoke(this, 1, new Object[]{}); + } + } catch (Exception e) { /** NON BLOCK **/} + } + } + +} diff --git a/res/layout/color_picker_pref_item.xml b/res/layout/color_picker_pref_item.xml new file mode 100644 index 00000000..02c44c1a --- /dev/null +++ b/res/layout/color_picker_pref_item.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="48dp" + android:layout_height="32dp" + android:background="@android:color/darker_gray"> + <afzkl.development.mColorPicker.views.ColorPanelView + android:id="@+android:id/color_picker" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="1dp" + android:layout_gravity="center" + android:focusable="false" + android:clickable="false" /> +</LinearLayout> + diff --git a/res/menu/editor.xml b/res/menu/editor.xml index 960d305d..654187ac 100644 --- a/res/menu/editor.xml +++ b/res/menu/editor.xml @@ -16,11 +16,21 @@ --> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item + android:id="@+id/mnu_no_suggestions" + android:showAsAction="never" + android:checkable="true" + android:title="@string/menu_no_suggestions"/> + <item android:id="@+id/mnu_word_wrap" android:showAsAction="never" android:checkable="true" android:title="@string/menu_word_wrap"/> <item + android:id="@+id/mnu_syntax_highlight" + android:showAsAction="never" + android:checkable="true" + android:title="@string/menu_syntax_highlight"/> + <item android:id="@+id/mnu_settings" android:showAsAction="never" android:title="@string/menu_settings"/> diff --git a/res/values/strings.xml b/res/values/strings.xml index 7f524ce0..c8566788 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -425,8 +425,12 @@ <string name="menu_settings">Settings</string> <!-- Menu * History * Clear history --> <string name="menu_clear_history">Clear history</string> + <!-- Menu * Editor * No suggestions --> + <string name="menu_no_suggestions">No suggestions</string> <!-- Menu * Editor * Word wrap --> <string name="menu_word_wrap">Word wrap</string> + <!-- Menu * Editor * Sintax highlight --> + <string name="menu_syntax_highlight">Syntax highlight</string> <!-- Regular expression for create copy action --> <string name="create_copy_regexp"> @@ -737,8 +741,34 @@ <string name="pref_remove_saved_search_terms_msg">All saved search terms were removed.</string> <!-- Preferences * Editor * Behaviour category --> <string name="pref_editor_behaviour_category">Behaviour</string> + <!-- Preferences * Editor * No suggestions --> + <string name="pref_no_suggestions">No suggestions</string> + <!-- Preferences * Editor * No suggestions summary --> + <string name="pref_no_suggestions_desc">Do not display dictionary suggestions while editing the file</string> <!-- Preferences * Editor * Word wrap --> <string name="pref_word_wrap">Word wrap</string> + <!-- Preferences * Editor * Hexdump --> + <string name="pref_hexdump">Hexdump binary files</string> + <!-- Preferences * Editor * Hexdump desc --> + <string name="pref_hexdump_desc">When opening a binary file, generate an hexdump of the file + and open it in the hex viewer.</string> + <!-- Preferences * Editor * Syntax highlight category --> + <string name="pref_editor_syntax_highlight_category">Syntax highlight</string> + <!-- Preferences * Editor * Syntax highlight --> + <string name="pref_syntax_highlight">Syntax highlight</string> + <!-- Preferences * Editor * Syntax highlight summary --> + <string name="pref_syntax_highlight_desc">Highlight the syntax of the file displayed in + the editor (only when a syntax highlight processor is available for the type of file)</string> + <!-- Preferences * Editor * Syntax highlight color scheme --> + <string name="pref_syntax_highlight_color_scheme">Color scheme</string> + <!-- Preferences * Editor * Syntax highlight color scheme summary --> + <string name="pref_syntax_highlight_color_scheme_desc">Tap to select the syntax highlight color scheme</string> + <!-- Preferences * Editor * Syntax highlight * Color Scheme --> + <string name="pref_syntax_sh_use_theme_default">Use theme default</string> + <!-- Preferences * Editor * Syntax highlight *Color Scheme summary --> + <string name="pref_syntax_sh_use_theme_default_desc">Use the default syntax highlight of the current theme</string> + <!-- Preferences * Editor * Syntax highlight * Color Scheme * Items category --> + <string name="pref_editor_sh_item_category">Items</string> <!-- Preferences * Themes * Themes selection category --> <string name="pref_themes_selection_category">Themes</string> <!-- Preferences * Themes * Set theme button --> @@ -760,6 +790,26 @@ <!-- Themes * Default theme author --> <string name="themes_author">CyanogenMod</string> + <!-- ColorPickerDialog --> + <!-- The text of the alpha slider control --> + <string name="color_picker_alpha_slider_text">Alpha</string> + <!-- The label of the current color panel --> + <string name="color_picker_current_text">Current:</string> + <!-- The label of the new color panel --> + <string name="color_picker_new_text">New:</string> + <!-- The label of the color input --> + <string name="color_picker_color">Color:</string> + + <!-- Android Syntax Highlight --> + <string name="ash_reset_color_scheme">Tap to restore the default theme color scheme</string> + <string name="ash_text">Text</string> + <string name="ash_assignment">Assignment</string> + <string name="ash_singleline_comment">Single-Line comment</string> + <string name="ash_multiline_comment">Multi-Line comment</string> + <string name="ash_keyword">Keyword</string> + <string name="ash_quoted_string">Quoted string</string> + <string name="ash_variable">Variable</string> + <!-- Security * Extract relative or absolute files --> <string name="security_warning_extract">Warning!\n\n Extracting an archive file with relative or absolute paths may cause damage to your device diff --git a/res/values/theme.xml b/res/values/theme.xml index 629b234c..eeb9b6b9 100644 --- a/res/values/theme.xml +++ b/res/values/theme.xml @@ -175,4 +175,14 @@ <drawable name="fso_type_text_drawable">@drawable/fso_type_text</drawable> <drawable name="fso_type_video_drawable">@drawable/fso_type_video</drawable> + + <!-- Syntax Highlight --> + <color name="ash_text_color">@color/black_transparent</color> + <color name="ash_assignment_color">@color/black_transparent</color> + <color name="ash_singleline_comment_color">#ff278556</color> + <color name="ash_multiline_comment_color">#ff7f9fbf</color> + <color name="ash_keyword_color">#ff773b63</color> + <color name="ash_quoted_string_color">#990000C0</color> + <color name="ash_variable_color">#ff5080bd</color> + </resources> diff --git a/res/xml/preferences_editor.xml b/res/xml/preferences_editor.xml index d4731dcc..350aa953 100644 --- a/res/xml/preferences_editor.xml +++ b/res/xml/preferences_editor.xml @@ -22,6 +22,14 @@ android:key="editor_behaviour" android:title="@string/pref_editor_behaviour_category"> + <!-- No suggestions --> + <CheckBoxPreference + android:key="cm_filemanager_editor_no_suggestions" + android:title="@string/pref_no_suggestions" + android:summary="@string/pref_no_suggestions_desc" + android:persistent="true" + android:defaultValue="false" /> + <!-- Word wrap --> <CheckBoxPreference android:key="cm_filemanager_editor_word_wrap" @@ -29,6 +37,37 @@ android:persistent="true" android:defaultValue="true" /> + <!-- Hexdump --> + <CheckBoxPreference + android:key="cm_filemanager_editor_hexdump" + android:title="@string/pref_hexdump" + android:summary="@string/pref_hexdump_desc" + android:persistent="true" + android:defaultValue="true" /> + + </PreferenceCategory> + + <!-- Editor Syntax Highlight --> + <PreferenceCategory + android:key="editor_syntax_highlight" + android:title="@string/pref_editor_syntax_highlight_category"> + + <!-- Syntax highlight --> + <CheckBoxPreference + android:key="cm_filemanager_editor_syntax_highlight" + android:title="@string/pref_syntax_highlight" + android:summary="@string/pref_syntax_highlight_desc" + android:persistent="true" + android:defaultValue="true" /> + + <!-- Color scheme --> + <PreferenceScreen + android:key="cm_filemanager_editor_sh_color_scheme" + android:title="@string/pref_syntax_highlight_color_scheme" + android:summary="@string/pref_syntax_highlight_color_scheme_desc" + android:dependency="cm_filemanager_editor_syntax_highlight" + android:fragment="com.cyanogenmod.filemanager.activities.preferences.EditorSHColorSchemePreferenceFragment" /> + </PreferenceCategory> </PreferenceScreen> diff --git a/res/xml/preferences_editor_color_scheme.xml b/res/xml/preferences_editor_color_scheme.xml new file mode 100644 index 00000000..6edd0e4c --- /dev/null +++ b/res/xml/preferences_editor_color_scheme.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- Use default theme color scheme --> + <CheckBoxPreference + android:key="cm_filemanager_editor_sh_use_theme_default" + android:title="@string/pref_syntax_sh_use_theme_default" + android:summary="@string/pref_syntax_sh_use_theme_default_desc" + android:persistent="true" + android:defaultValue="true" /> + + <!-- Editor behaviour --> + <PreferenceCategory + android:key="editor_sh_items_category" + android:title="@string/pref_editor_sh_item_category"> + + <!-- Reset color scheme to theme defaults --> + <Preference + android:key="ash_reset_color_scheme" + android:summary="@string/ash_reset_color_scheme" + android:persistent="false" /> + + <!-- Text --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_text" + android:title="@string/ash_text" + android:persistent="false" + android:defaultValue="@color/ash_text_color" /> + + <!-- Assignment --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_assignment" + android:title="@string/ash_assignment" + android:persistent="false" + android:defaultValue="@color/ash_assignment_color" /> + + <!-- Single-Line comment --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_singleline_comment" + android:title="@string/ash_singleline_comment" + android:persistent="false" + android:defaultValue="@color/ash_singleline_comment_color" /> + + <!-- Multi-Line comment --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_multiline_comment" + android:title="@string/ash_multiline_comment" + android:persistent="false" + android:defaultValue="@color/ash_multiline_comment_color" /> + + <!-- Keyword --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_keyword" + android:title="@string/ash_keyword" + android:persistent="false" + android:defaultValue="@color/ash_keyword_color" /> + + <!-- Quoted string --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_quoted_string" + android:title="@string/ash_quoted_string" + android:persistent="false" + android:defaultValue="@color/ash_quoted_string_color" /> + + <!-- Variable --> + <com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference + android:key="ash_variable" + android:title="@string/ash_variable" + android:persistent="false" + android:defaultValue="@color/ash_variable_color" /> + + </PreferenceCategory> + +</PreferenceScreen> diff --git a/res/xml/preferences_headers.xml b/res/xml/preferences_headers.xml index a28e5842..80c4509d 100644 --- a/res/xml/preferences_headers.xml +++ b/res/xml/preferences_headers.xml @@ -16,16 +16,16 @@ <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header - android:fragment="com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences$GeneralPreferenceFragment" + android:fragment="com.cyanogenmod.filemanager.activities.preferences.GeneralPreferenceFragment" android:title="@string/pref_general" /> <header - android:fragment="com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences$SearchPreferenceFragment" + android:fragment="com.cyanogenmod.filemanager.activities.preferences.SearchPreferenceFragment" android:title="@string/pref_search" /> <header - android:fragment="com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences$EditorPreferenceFragment" + android:fragment="com.cyanogenmod.filemanager.activities.preferences.EditorPreferenceFragment" android:title="@string/pref_editor" /> <header - android:fragment="com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences$ThemesPreferenceFragment" + android:fragment="com.cyanogenmod.filemanager.activities.preferences.ThemesPreferenceFragment" android:title="@string/pref_themes" /> <header android:title="@string/pref_about" diff --git a/src/com/cyanogenmod/filemanager/FileManagerApplication.java b/src/com/cyanogenmod/filemanager/FileManagerApplication.java index cae025e2..3e77be46 100644 --- a/src/com/cyanogenmod/filemanager/FileManagerApplication.java +++ b/src/com/cyanogenmod/filemanager/FileManagerApplication.java @@ -314,7 +314,7 @@ public final class FileManagerApplication extends Application { * @return boolean If the command is present */ public static boolean hasOptionalCommand(String commandId) { - if (!sOptionalCommandsMap.containsKey(commandId)){ + if (!sOptionalCommandsMap.containsKey(commandId)) { return false; } return sOptionalCommandsMap.get(commandId).booleanValue(); diff --git a/src/com/cyanogenmod/filemanager/activities/EditorActivity.java b/src/com/cyanogenmod/filemanager/activities/EditorActivity.java index 528ce49d..47da72e5 100644 --- a/src/com/cyanogenmod/filemanager/activities/EditorActivity.java +++ b/src/com/cyanogenmod/filemanager/activities/EditorActivity.java @@ -30,8 +30,11 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.preference.PreferenceActivity; import android.text.Editable; +import android.text.InputType; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; @@ -51,10 +54,15 @@ import android.widget.Toast; import com.android.internal.util.HexDump; import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.activities.preferences.EditorPreferenceFragment; +import com.cyanogenmod.filemanager.activities.preferences.EditorSHColorSchemePreferenceFragment; import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences; -import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences.EditorPreferenceFragment; import com.cyanogenmod.filemanager.adapters.HighlightedSimpleMenuListAdapter; import com.cyanogenmod.filemanager.adapters.SimpleMenuListAdapter; +import com.cyanogenmod.filemanager.ash.HighlightColors; +import com.cyanogenmod.filemanager.ash.ISyntaxHighlightResourcesResolver; +import com.cyanogenmod.filemanager.ash.SyntaxHighlightFactory; +import com.cyanogenmod.filemanager.ash.SyntaxHighlightProcessor; import com.cyanogenmod.filemanager.commands.AsyncResultListener; import com.cyanogenmod.filemanager.commands.WriteExecutable; import com.cyanogenmod.filemanager.console.ConsoleBuilder; @@ -70,6 +78,7 @@ import com.cyanogenmod.filemanager.util.DialogHelper; import com.cyanogenmod.filemanager.util.ExceptionUtil; import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult; import com.cyanogenmod.filemanager.util.FileHelper; +import com.cyanogenmod.filemanager.util.ResourcesHelper; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -99,17 +108,81 @@ public class EditorActivity extends Activity implements TextWatcher { // The settings has changed String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY); if (key != null) { + final EditorActivity activity = EditorActivity.this; + + // No suggestions + if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId()) == 0) { + // Ignore in binary files + if (activity.mBinary) return; + + // Do we have a different setting? + boolean noSuggestionsSetting = + Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(), + ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS. + getDefaultValue()).booleanValue()); + if (noSuggestionsSetting != activity.mNoSuggestions) { + activity.mHandler.post(new Runnable() { + @Override + public void run() { + toggleNoSuggestions(); + } + }); + } + // Word wrap - if (key.compareTo(FileManagerSettings. - SETTINGS_EDITOR_WORD_WRAP.getId()) == 0) { + } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId()) == 0) { + // Ignore in binary files + if (activity.mBinary) return; + // Do we have a different setting? boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(), ((Boolean)FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP. getDefaultValue()).booleanValue()); - if (wordWrapSetting != EditorActivity.this.mWordWrap) { - toggleWordWrap(); + if (wordWrapSetting != activity.mWordWrap) { + activity.mHandler.post(new Runnable() { + @Override + public void run() { + toggleWordWrap(); + } + }); + } + + // Syntax highlight + // Default theme color scheme + // Color scheme + } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId()) == 0) { + // Ignore in binary files + if (activity.mBinary) return; + + // Do we have a different setting? + boolean syntaxHighlightSetting = + Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(), + ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT. + getDefaultValue()).booleanValue()); + if (syntaxHighlightSetting != activity.mSyntaxHighlight) { + activity.mHandler.post(new Runnable() { + @Override + public void run() { + toggleSyntaxHighlight(); + } + }); } + + } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()) == 0 || + key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId()) == 0 ) { + // Ignore in binary files + if (activity.mBinary) return; + + // Reload the syntax highlight + activity.mHandler.post(new Runnable() { + @Override + public void run() { + reloadSyntaxHighlight(); + } + }); } } return; @@ -128,15 +201,14 @@ public class EditorActivity extends Activity implements TextWatcher { /** * An internal listener for read a file */ - @SuppressWarnings("hiding") private class AsyncReader implements AsyncResultListener { final Object mSync = new Object(); ByteArrayOutputStream mByteBuffer = null; - StringBuilder mBuffer = null; + SpannableStringBuilder mBuffer = null; Exception mCause; long mSize; - FileSystemObject mFso; + FileSystemObject mReadFso; OnProgressListener mListener; /** @@ -151,7 +223,7 @@ public class EditorActivity extends Activity implements TextWatcher { */ @Override public void onAsyncStart() { - this.mByteBuffer = new ByteArrayOutputStream((int)this.mFso.getSize()); + this.mByteBuffer = new ByteArrayOutputStream((int)this.mReadFso.getSize()); this.mSize = 0; } @@ -194,10 +266,10 @@ public class EditorActivity extends Activity implements TextWatcher { this.mByteBuffer.write(partial, 0, partial.length); this.mSize += partial.length; - if (this.mListener != null && this.mFso != null) { + if (this.mListener != null && this.mReadFso != null) { int progress = 0; - if (this.mFso.getSize() != 0) { - progress = (int)((this.mSize*100) / this.mFso.getSize()); + if (this.mReadFso.getSize() != 0) { + progress = (int)((this.mSize*100) / this.mReadFso.getSize()); } this.mListener.onProgress(progress); } @@ -263,6 +335,89 @@ public class EditorActivity extends Activity implements TextWatcher { } /** + * An internal class to resolve resources for the syntax highlight library. + * @hide + */ + class ResourcesResolver implements ISyntaxHighlightResourcesResolver { + @Override + public CharSequence getString(String id, String resid) { + return EditorActivity.this.getString( + ResourcesHelper.getIdentifier( + EditorActivity.this.getResources(), "string", resid)); //$NON-NLS-1$ + } + + @Override + public int getInteger(String id, String resid, int def) { + return EditorActivity.this.getResources(). + getInteger( + ResourcesHelper.getIdentifier( + EditorActivity.this.getResources(), "integer", resid)); //$NON-NLS-1$ + } + + @Override + public int getColor(String id, String resid, int def) { + final Context ctx = EditorActivity.this; + try { + // Is default theme color scheme enabled? + if (isDefaultThemeColorScheme()) { + return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid); + } + + // Use the user-defined settings + int[] colors = getUserColorScheme(); + HighlightColors[] schemeColors = HighlightColors.values(); + int cc = schemeColors.length; + int cc2 = colors.length; + for (int i = 0; i < cc; i++) { + if (schemeColors[i].getId().compareTo(id) == 0) { + if (cc2 >= i) { + // User-defined + return colors[i]; + } + + // Theme default + return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid); + } + + } + + } catch (Exception ex) { + // Resource not found + } + return def; + } + + /** + * Method that returns if we should return the default theme color scheme or not + * + * @return boolean Whether return the default theme color scheme or not + */ + private boolean isDefaultThemeColorScheme() { + Boolean defaultValue = + (Boolean)FileManagerSettings. + SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getDefaultValue(); + return Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId(), + defaultValue.booleanValue()); + } + + /** + * Method that returns the user-defined color scheme + * + * @return int[] The user-defined color scheme + */ + private int[] getUserColorScheme() { + String defaultValue = + (String)FileManagerSettings. + SETTINGS_EDITOR_SH_COLOR_SCHEME.getDefaultValue(); + String value = Preferences.getSharedPreferences().getString( + FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(), + defaultValue); + return EditorSHColorSchemePreferenceFragment.toColorShemeArray(value); + } + } + + /** * @hide */ FileSystemObject mFso; @@ -308,6 +463,12 @@ public class EditorActivity extends Activity implements TextWatcher { */ ButtonItem mSave; + // No suggestions status + /** + * @hide + */ + boolean mNoSuggestions; + // Word wrap status private ViewGroup mWordWrapView; private ViewGroup mNoWordWrapView; @@ -316,8 +477,27 @@ public class EditorActivity extends Activity implements TextWatcher { */ boolean mWordWrap; + // Syntax highlight status + /** + * @hide + */ + boolean mSyntaxHighlight; + /** + * @hide + */ + SyntaxHighlightProcessor mSyntaxHighlightProcessor; + private int mEditStart; + private int mEditEnd; + private View mOptionsAnchorView; + private final Object mExecSync = new Object(); + + /** + * @hide + */ + Handler mHandler; + private static final char[] VALID_NON_PRINTABLE_CHARS = {' ', '\t', '\r', '\n'}; /** @@ -339,6 +519,8 @@ public class EditorActivity extends Activity implements TextWatcher { Log.d(TAG, "EditorActivity.onCreate"); //$NON-NLS-1$ } + this.mHandler = new Handler(); + // Register the broadcast receiver IntentFilter filter = new IntentFilter(); filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED); @@ -443,10 +625,22 @@ public class EditorActivity extends Activity implements TextWatcher { this.mEditor.setEnabled(false); this.mWordWrapView = (ViewGroup)findViewById(R.id.editor_word_wrap_view); this.mNoWordWrapView = (ViewGroup)findViewById(R.id.editor_no_word_wrap_view); - this.mWordWrap = true; this.mWordWrapView.setVisibility(View.VISIBLE); this.mNoWordWrapView.setVisibility(View.GONE); + this.mNoSuggestions = false; + this.mWordWrap = true; + this.mSyntaxHighlight = true; + + // Load the no suggestions setting + boolean noSuggestionsSetting = Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(), + ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS. + getDefaultValue()).booleanValue()); + if (noSuggestionsSetting != this.mNoSuggestions) { + toggleNoSuggestions(); + } + // Load the word wrap setting boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(), @@ -456,30 +650,99 @@ public class EditorActivity extends Activity implements TextWatcher { toggleWordWrap(); } + // Load the syntax highlight setting + boolean syntaxHighlighSetting = Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(), + ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT. + getDefaultValue()).booleanValue()); + if (syntaxHighlighSetting != this.mSyntaxHighlight) { + toggleSyntaxHighlight(); + } + this.mProgress = findViewById(R.id.editor_progress); this.mProgressBar = (ProgressBar)findViewById(R.id.editor_progress_bar); this.mProgressBarMsg = (TextView)findViewById(R.id.editor_progress_msg); } /** + * Method that toggle the no suggestions property of the editor + * @hide + */ + /**package**/ void toggleNoSuggestions() { + synchronized (this.mExecSync) { + int type = InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_MULTI_LINE | + InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE; + if (!this.mNoSuggestions) { + type |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + this.mEditor.setInputType(type); + this.mNoSuggestions = !this.mNoSuggestions; + } + } + + /** * Method that toggle the word wrap property of the editor * @hide */ /**package**/ void toggleWordWrap() { - ViewGroup vSrc = this.mWordWrap ? this.mWordWrapView : this.mNoWordWrapView; - ViewGroup vDst = this.mWordWrap ? this.mNoWordWrapView : this.mWordWrapView; - ViewGroup vSrcParent = this.mWordWrap - ? this.mWordWrapView - : (ViewGroup)this.mNoWordWrapView.getChildAt(0); - ViewGroup vDstParent = this.mWordWrap - ? (ViewGroup)this.mNoWordWrapView.getChildAt(0) - : this.mWordWrapView; - vSrc.setVisibility(View.GONE); - vSrcParent.removeView(this.mEditor); - vDstParent.addView(this.mEditor); - vDst.setVisibility(View.VISIBLE); - vDst.scrollTo(0, 0); - this.mWordWrap = !this.mWordWrap; + synchronized (this.mExecSync) { + ViewGroup vSrc = this.mWordWrap ? this.mWordWrapView : this.mNoWordWrapView; + ViewGroup vDst = this.mWordWrap ? this.mNoWordWrapView : this.mWordWrapView; + ViewGroup vSrcParent = this.mWordWrap + ? this.mWordWrapView + : (ViewGroup)this.mNoWordWrapView.getChildAt(0); + ViewGroup vDstParent = this.mWordWrap + ? (ViewGroup)this.mNoWordWrapView.getChildAt(0) + : this.mWordWrapView; + vSrc.setVisibility(View.GONE); + vSrcParent.removeView(this.mEditor); + vDstParent.addView(this.mEditor); + vDst.setVisibility(View.VISIBLE); + vDst.scrollTo(0, 0); + this.mWordWrap = !this.mWordWrap; + } + } + + /** + * Method that toggles the syntax highlight property of the editor + * @hide + */ + /**package**/ void toggleSyntaxHighlight() { + synchronized (this.mExecSync) { + if (this.mSyntaxHighlightProcessor != null) { + try { + if (this.mSyntaxHighlight) { + this.mSyntaxHighlightProcessor.clear(this.mEditor.getText()); + } else { + this.mSyntaxHighlightProcessor.process(this.mEditor.getText()); + } + } catch (Exception ex) { + // An error in a syntax library, should not break down app. + Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$ + } + } + + this.mSyntaxHighlight = !this.mSyntaxHighlight; + } + } + + /** + * Method that reloads the syntax highlight of the current file + * @hide + */ + /**package**/ void reloadSyntaxHighlight() { + synchronized (this.mExecSync) { + if (this.mSyntaxHighlightProcessor != null) { + try { + this.mSyntaxHighlightProcessor.initialize(); + this.mSyntaxHighlightProcessor.process(this.mEditor.getText()); + } catch (Exception ex) { + // An error in a syntax library, should not break down app. + Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$ + } + } + } } /** @@ -524,6 +787,14 @@ public class EditorActivity extends Activity implements TextWatcher { private void showOverflowPopUp(View anchor) { SimpleMenuListAdapter adapter = new HighlightedSimpleMenuListAdapter(this, R.menu.editor); + MenuItem noSuggestions = adapter.getMenu().findItem(R.id.mnu_no_suggestions); + if (noSuggestions != null) { + if (this.mBinary) { + adapter.getMenu().removeItem(R.id.mnu_no_suggestions); + } else { + noSuggestions.setChecked(this.mNoSuggestions); + } + } MenuItem wordWrap = adapter.getMenu().findItem(R.id.mnu_word_wrap); if (wordWrap != null) { if (this.mBinary) { @@ -532,6 +803,14 @@ public class EditorActivity extends Activity implements TextWatcher { wordWrap.setChecked(this.mWordWrap); } } + MenuItem syntaxHighlight = adapter.getMenu().findItem(R.id.mnu_syntax_highlight); + if (syntaxHighlight != null) { + if (this.mBinary) { + adapter.getMenu().removeItem(R.id.mnu_syntax_highlight); + } else { + syntaxHighlight.setChecked(this.mSyntaxHighlight); + } + } final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor); @@ -542,10 +821,15 @@ public class EditorActivity extends Activity implements TextWatcher { final int position, final long id) { final int itemId = (int)id; switch (itemId) { + case R.id.mnu_no_suggestions: + toggleNoSuggestions(); + break; case R.id.mnu_word_wrap: - popup.dismiss(); toggleWordWrap(); break; + case R.id.mnu_syntax_highlight: + toggleSyntaxHighlight(); + break; case R.id.mnu_settings: //Settings Intent settings = new Intent(EditorActivity.this, SettingsPreferences.class); @@ -654,6 +938,14 @@ public class EditorActivity extends Activity implements TextWatcher { return; } + // Get the syntax highlight processor + SyntaxHighlightFactory shpFactory = + SyntaxHighlightFactory.getDefaultFactory(new ResourcesResolver()); + this.mSyntaxHighlightProcessor = shpFactory.getSyntaxHighlightProcessor(f); + if (this.mSyntaxHighlightProcessor != null) { + this.mSyntaxHighlightProcessor.initialize(); + } + // Check that we have read access try { FileHelper.ensureReadAccess( @@ -710,6 +1002,8 @@ public class EditorActivity extends Activity implements TextWatcher { @Override protected Boolean doInBackground(FileSystemObject... params) { + final EditorActivity activity = EditorActivity.this; + // Only one argument (the file to open) FileSystemObject fso = params[0]; this.mCause = null; @@ -719,7 +1013,7 @@ public class EditorActivity extends Activity implements TextWatcher { while (true) { // Configure the reader this.mReader = new AsyncReader(); - this.mReader.mFso = fso; + this.mReader.mReadFso = fso; this.mReader.mListener = new OnProgressListener() { @Override @SuppressWarnings("synthetic-access") @@ -729,8 +1023,7 @@ public class EditorActivity extends Activity implements TextWatcher { }; // Execute the command (read the file) - CommandHelper.read( - EditorActivity.this, fso.getFullPath(), this.mReader, null); + CommandHelper.read(activity, fso.getFullPath(), this.mReader, null); // Wait for synchronized (this.mReader.mSync) { @@ -749,26 +1042,31 @@ public class EditorActivity extends Activity implements TextWatcher { } // Now we have the byte array with all the data. is a binary file? - // Then dump them byte array to hex dump string - // Don't use the Hexdump helper class, so we can show the progress of - // the dump process - if (EditorActivity.this.mBinary) { + // Then dump them byte array to hex dump string (only if users settings + // to dump file) + boolean hexDump = + Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.getId(), + ((Boolean)FileManagerSettings.SETTINGS_EDITOR_HEXDUMP. + getDefaultValue()).booleanValue()); + if (activity.mBinary && hexDump) { + // we do not use the Hexdump helper class, because we need to show the + // progress of the dump process this.mReader.mBuffer = - new StringBuilder( + new SpannableStringBuilder( toHexPrintableString( toHexDump( this.mReader.mByteBuffer.toByteArray()))); } else { this.mReader.mBuffer = - new StringBuilder( + new SpannableStringBuilder( new String(this.mReader.mByteBuffer.toByteArray())); } this.mReader.mByteBuffer = null; - // 100% - We need two calls here to proper display the message + // 100% this.changeToDisplaying = true; publishProgress(new Integer(0)); - publishProgress(new Integer(0)); } catch (Exception e) { this.mCause = e; @@ -786,29 +1084,43 @@ public class EditorActivity extends Activity implements TextWatcher { @Override protected void onPostExecute(Boolean result) { + final EditorActivity activity = EditorActivity.this; + // Is error? if (!result.booleanValue()) { if (this.mCause != null) { - ExceptionUtil.translateException(EditorActivity.this, this.mCause); - EditorActivity.this.mEditor.setEnabled(false); + ExceptionUtil.translateException(activity, this.mCause); + activity.mEditor.setEnabled(false); } } else { // Now we have the buffer, set the text of the editor - if (EditorActivity.this.mBinary) { - EditorActivity.this.mEditor.setText( + if (activity.mBinary) { + activity.mEditor.setText( this.mReader.mBuffer, BufferType.NORMAL); } else { - EditorActivity.this.mEditor.setText( + activity.mEditor.setText( this.mReader.mBuffer, BufferType.EDITABLE); + + // Highlight editor text syntax + if (activity.mSyntaxHighlight && + activity.mSyntaxHighlightProcessor != null) { + try { + activity.mSyntaxHighlightProcessor.process( + activity.mEditor.getText()); + } catch (Exception ex) { + // An error in a syntax library, should not break down app. + Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$ + } + } } this.mReader.mBuffer = null; //Cleanup setDirty(false); - EditorActivity.this.mEditor.setEnabled(!EditorActivity.this.mReadOnly); + activity.mEditor.setEnabled(!activity.mReadOnly); // Notify read-only mode - if (EditorActivity.this.mReadOnly) { + if (activity.mReadOnly) { DialogHelper.showToast( - EditorActivity.this, + activity, R.string.editor_read_only_mode, Toast.LENGTH_SHORT); } @@ -830,26 +1142,30 @@ public class EditorActivity extends Activity implements TextWatcher { * @param progress The progress */ private void doProgress(boolean visible, int progress) { + final EditorActivity activity = EditorActivity.this; + // Show the progress bar - EditorActivity.this.mProgressBar.setProgress(progress); - EditorActivity.this.mProgress.setVisibility( - visible ? View.VISIBLE : View.GONE); + activity.mProgressBar.setProgress(progress); + activity.mProgress.setVisibility(visible ? View.VISIBLE : View.GONE); if (this.changeToBinaryMode) { // Hexdump always in nowrap mode - if (EditorActivity.this.mWordWrap) { - EditorActivity.this.toggleWordWrap(); + if (activity.mWordWrap) { + activity.toggleWordWrap(); + } + // Hexdump always has no syntax highlight + if (activity.mSyntaxHighlight) { + activity.toggleSyntaxHighlight(); } // Show hex dumping text - EditorActivity.this.mProgressBarMsg.setText(R.string.dumping_message); - EditorActivity.this.mEditor.setTextAppearance( - EditorActivity.this, R.style.hexeditor_text_appearance); - EditorActivity.this.mEditor.setTypeface(Typeface.MONOSPACE); + activity.mProgressBarMsg.setText(R.string.dumping_message); + activity.mEditor.setTextAppearance(activity, R.style.hexeditor_text_appearance); + activity.mEditor.setTypeface(Typeface.MONOSPACE); this.changeToBinaryMode = false; } else if (this.changeToDisplaying) { - EditorActivity.this.mProgressBarMsg.setText(R.string.displaying_message); + activity.mProgressBarMsg.setText(R.string.displaying_message); this.changeToDisplaying = false; } } @@ -1030,7 +1346,10 @@ public class EditorActivity extends Activity implements TextWatcher { * {@inheritDoc} */ @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {/**NON BLOCK**/} + public void onTextChanged(CharSequence s, int start, int before, int count) { + this.mEditStart = start; + this.mEditEnd = start + count; + } /** * {@inheritDoc} @@ -1038,6 +1357,9 @@ public class EditorActivity extends Activity implements TextWatcher { @Override public void afterTextChanged(Editable s) { setDirty(true); + if (this.mSyntaxHighlightProcessor != null) { + this.mSyntaxHighlightProcessor.process(s, this.mEditStart, this.mEditEnd); + } } /** @@ -1117,6 +1439,11 @@ public class EditorActivity extends Activity implements TextWatcher { //- ProgressBar Drawable dw = theme.getDrawable(this, "horizontal_progress_bar"); //$NON-NLS-1$ this.mProgressBar.setProgressDrawable(dw); + + // Need a full process of syntax highlight + if (!this.mBinary && this.mSyntaxHighlight && this.mSyntaxHighlightProcessor != null) { + reloadSyntaxHighlight(); + } } } diff --git a/src/com/cyanogenmod/filemanager/activities/PickerActivity.java b/src/com/cyanogenmod/filemanager/activities/PickerActivity.java index 3c8d179c..6c6b9961 100644 --- a/src/com/cyanogenmod/filemanager/activities/PickerActivity.java +++ b/src/com/cyanogenmod/filemanager/activities/PickerActivity.java @@ -425,7 +425,7 @@ public class PickerActivity extends Activity } } - private boolean isFilePickIntent(Intent intent) { + private static boolean isFilePickIntent(Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_GET_CONTENT.equals(action)) { @@ -441,7 +441,7 @@ public class PickerActivity extends Activity return false; } - private boolean isDirectoryPickIntent(Intent intent) { + private static boolean isDirectoryPickIntent(Intent intent) { if (Intent.ACTION_PICK.equals(intent.getAction()) && intent.getData() != null) { String scheme = intent.getData().getScheme(); if (FOLDER_URI_SCHEME.equals(scheme) || DIRECTORY_URI_SCHEME.equals(scheme)) { @@ -452,7 +452,7 @@ public class PickerActivity extends Activity return false; } - private File getInitialDirectoryFromIntent(Intent intent) { + private static File getInitialDirectoryFromIntent(Intent intent) { if (!Intent.ACTION_PICK.equals(intent.getAction())) { return null; } @@ -478,7 +478,7 @@ public class PickerActivity extends Activity return file.getParentFile(); } - private Uri getResultUriForFileFromIntent(File src, Intent intent) { + private static Uri getResultUriForFileFromIntent(File src, Intent intent) { Uri result = Uri.fromFile(src); if (Intent.ACTION_PICK.equals(intent.getAction()) && intent.getData() != null) { diff --git a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java index 60d043c8..75d528e4 100644 --- a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java +++ b/src/com/cyanogenmod/filemanager/activities/SearchActivity.java @@ -48,8 +48,8 @@ import android.widget.Toast; import com.cyanogenmod.filemanager.FileManagerApplication; import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.activities.preferences.SearchPreferenceFragment; import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences; -import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences.SearchPreferenceFragment; import com.cyanogenmod.filemanager.adapters.SearchResultAdapter; import com.cyanogenmod.filemanager.commands.AsyncResultExecutable; import com.cyanogenmod.filemanager.commands.AsyncResultListener; diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/EditorPreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/EditorPreferenceFragment.java new file mode 100644 index 00000000..03836c56 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/EditorPreferenceFragment.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.util.Log; + +import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.preferences.FileManagerSettings; +import com.cyanogenmod.filemanager.preferences.Preferences; + +/** + * A class that manages the editor options + */ +public class EditorPreferenceFragment extends TitlePreferenceFragment { + + private static final String TAG = "EditorPreferenceFragment"; //$NON-NLS-1$ + + private static final boolean DEBUG = false; + + private CheckBoxPreference mNoSuggestions; + private CheckBoxPreference mWordWrap; + private CheckBoxPreference mHexdump; + + private CheckBoxPreference mSyntaxHighlight; + + + /** + * @hide + */ + boolean mLoaded = false; + + private final OnPreferenceChangeListener mOnChangeListener = + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + boolean ret = true; + + String key = preference.getKey(); + if (DEBUG) { + Log.d(TAG, + String.format("New value for %s: %s", //$NON-NLS-1$ + key, + String.valueOf(newValue))); + } + + // Notify the change (only if fragment is loaded. Default values are loaded + // while not in loaded mode) + if (EditorPreferenceFragment.this.mLoaded && ret) { + Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); + intent.putExtra( + FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); + getActivity().sendBroadcast(intent); + } + + return ret; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + this.mLoaded = false; + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_editor); + + // No suggestions + this.mNoSuggestions = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId()); + this.mNoSuggestions.setOnPreferenceChangeListener(this.mOnChangeListener); + + // WordWrap + this.mWordWrap = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId()); + this.mWordWrap.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Hexdump + this.mHexdump = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.getId()); + this.mHexdump.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Syntax highlight + this.mSyntaxHighlight = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId()); + this.mSyntaxHighlight.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Loaded + this.mLoaded = true; + } + + /** + * {@inheritDoc} + */ + @Override + public CharSequence getTitle() { + return getString(R.string.pref_editor); + } +} diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/EditorSHColorSchemePreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/EditorSHColorSchemePreferenceFragment.java new file mode 100644 index 00000000..4dbababd --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/EditorSHColorSchemePreferenceFragment.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.text.TextUtils; +import android.util.Log; + +import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.ash.HighlightColors; +import com.cyanogenmod.filemanager.preferences.FileManagerSettings; +import com.cyanogenmod.filemanager.preferences.Preferences; +import com.cyanogenmod.filemanager.ui.ThemeManager; +import com.cyanogenmod.filemanager.ui.preferences.ColorPickerPreference; +import com.cyanogenmod.filemanager.util.ExceptionUtil; + +/** + * A class that manages the color scheme of the syntax highlight processor. + */ +public class EditorSHColorSchemePreferenceFragment extends TitlePreferenceFragment { + + private static final String TAG = "EditorSHColorSchemePreferenceFragment"; //$NON-NLS-1$ + + private static final boolean DEBUG = false; + + private static final String KEY_RESET_COLOR_SCHEME = "ash_reset_color_scheme"; //$NON-NLS-1$ + + private CheckBoxPreference mUseThemeDefault; + private Preference mResetColorScheme; + private ColorPickerPreference[] mColorScheme; + + /** + * @hide + */ + boolean mLoaded = false; + + private final OnPreferenceChangeListener mOnChangeListener = + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, final Object newValue) { + boolean ret = true; + + String key = preference.getKey(); + if (DEBUG) { + Log.d(TAG, + String.format("New value for %s: %s", //$NON-NLS-1$ + key, + String.valueOf(newValue))); + } + + // Use theme default + if (key.compareTo( + FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()) == 0) { + boolean enabled = ((Boolean)newValue).booleanValue(); + setColorSchemeEnabled(!enabled); + + } else if (isColorSchemePreference(preference)) { + // Unify the color schemes property. Save the property here + int color = ((Integer)newValue).intValue(); + try { + String colorScheme = toColorSchemeSet(preference, color); + Preferences.savePreference( + FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME, + colorScheme, + true); + } catch (Exception e) { + ExceptionUtil.translateException(getActivity(), e); + } + ((ColorPickerPreference)preference).setColor(color); + + // Change the key to get notifications of color scheme + key = FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(); + } + + // Notify the change (only if fragment is loaded. Default values are loaded + // while not in loaded mode) + if (EditorSHColorSchemePreferenceFragment.this.mLoaded && ret) { + Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); + intent.putExtra( + FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, key); + getActivity().sendBroadcast(intent); + } + + return ret; + } + }; + + private final OnPreferenceClickListener mOnClickListener = + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + String key = preference.getKey(); + if (KEY_RESET_COLOR_SCHEME.compareTo(key) == 0) { + loadDefaultColorScheme(true); + } + return false; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_editor_color_scheme); + + // Color scheme (need to resolver color scheme prior to use theme default) + loadDefaultColorScheme(false); + + // Use Theme default + this.mUseThemeDefault = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()); + Boolean defaultValue = ((Boolean)FileManagerSettings. + SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getDefaultValue()); + Boolean value = + Boolean.valueOf( + Preferences.getSharedPreferences().getBoolean( + FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId(), + defaultValue.booleanValue())); + + // Reset to default theme color scheme + this.mResetColorScheme = findPreference(KEY_RESET_COLOR_SCHEME); + + // Now the listeners + this.mOnChangeListener.onPreferenceChange(this.mUseThemeDefault, value); + this.mUseThemeDefault.setOnPreferenceChangeListener(this.mOnChangeListener); + this.mResetColorScheme.setOnPreferenceClickListener(this.mOnClickListener); + + // Loaded + this.mLoaded = true; + } + + /** + * {@inheritDoc} + */ + @Override + public CharSequence getTitle() { + return getString(R.string.pref_syntax_highlight_color_scheme); + } + + /** + * Method that loads the default color scheme + * + * @param reset Whether the color scheme should be reseted + * @hide + */ + void loadDefaultColorScheme(boolean reset) { + try { + String defaultValue = + (String)FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getDefaultValue(); + if (!reset) { + defaultValue = + Preferences.getSharedPreferences().getString( + FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(), + defaultValue); + } else { + Preferences.savePreference( + FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME, + defaultValue, + true); + } + int[] colorScheme = toColorShemeArray(defaultValue); + HighlightColors[] colors = HighlightColors.values(); + int cc = colors.length; + this.mColorScheme = new ColorPickerPreference[cc]; + for (int i = 0; i < cc; i++) { + this.mColorScheme[i] = (ColorPickerPreference)findPreference(colors[i].getId()); + setColorScheme(colors[i], colorScheme, i); + this.mColorScheme[i].setOnPreferenceChangeListener(this.mOnChangeListener); + } + } catch (Exception e) { + ExceptionUtil.translateException(getActivity(), e); + } + } + + /** + * Method that set the enabled status of the color schemes preferences + * + * @param enable If the color scheme preferences should be enabled or not. + * @hide + */ + void setColorSchemeEnabled(final boolean enable) { + int cc = this.mColorScheme.length; + for (int i = 0; i < cc; i++) { + this.mColorScheme[i].setEnabled(enable); + } + this.mResetColorScheme.setEnabled(enable); + } + + /** + * Method that set a color scheme (use setting or theme default) + * + * @param color The color reference + * @param colorScheme The array of colors + * @param pos The position of the color + * @hide + */ + void setColorScheme(HighlightColors color, int[] colorScheme, int pos) { + try { + this.mColorScheme[pos].setColor(colorScheme[pos]); + } catch (Exception e) { + this.mColorScheme[pos].setColor( + ThemeManager.getCurrentTheme( + getActivity()).getColor(getActivity(), color.getResId())); + Log.w(TAG, + String.format( + "Color scheme value not found for \"%s\"", //$NON-NLS-1$ + color.getId())); + } + } + + /** + * Method that returns if the preference is part of the color scheme preferences + * + * @return boolean Whether preference is part of the color scheme preferences + * @hide + */ + static boolean isColorSchemePreference(final Preference preference) { + String key = preference.getKey(); + if (key == null) { + return false; + } + HighlightColors[] colors = HighlightColors.values(); + int cc = colors.length; + for (int i = 0; i < cc; i++) { + if (colors[i].getId().compareTo(key) == 0) { + return true; + } + } + return false; + } + + /** + * Method that converts the string set of color schemes to an array of colors + * + * @param value The string set of color schemes to parse + * @return int[] Array of colors + */ + public static int[] toColorShemeArray(String value) { + if (value == null || value.length() == 0) { + return new int[]{}; + } + String[] values = value.split("\\|"); //$NON-NLS-1$ + int[] colors = new int[values.length]; + int cc = colors.length; + for (int i = 0; i < cc; i++) { + try { + colors[i] = Integer.parseInt(values[i]); + } catch (Exception e) { + Log.w(TAG, + String.format( + "Problem parsing color value \"%s\" on position %d", //$NON-NLS-1$ + values[i], Integer.valueOf(i))); + colors[i] = 0; + } + } + return colors; + } + + /** + * Method that converts all the color scheme preference to one unified preference set + * + * @param preference The color scheme preference that was changed + * @param newValue The new value of the color scheme + * @return colorScheme The actual color schemes + * @hide + */ + String toColorSchemeSet(final Preference preference, final int newValue) { + int cc = this.mColorScheme.length; + String[] colorSchemes = new String[cc]; + for (int i = 0; i < cc; i++) { + String prop = String.valueOf(this.mColorScheme[i].getColor()); + if (this.mColorScheme[i].getKey().compareTo(preference.getKey()) == 0) { + prop = String.valueOf(newValue); + } + colorSchemes[i] = prop; + } + return TextUtils.join("|", colorSchemes); //$NON-NLS-1$ + } +} diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java new file mode 100644 index 00000000..1e98105b --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/GeneralPreferenceFragment.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.util.Log; + +import com.cyanogenmod.filemanager.FileManagerApplication; +import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.console.ConsoleBuilder; +import com.cyanogenmod.filemanager.preferences.AccessMode; +import com.cyanogenmod.filemanager.preferences.FileManagerSettings; +import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier; +import com.cyanogenmod.filemanager.preferences.Preferences; + +/** + * A class that manages the commons options of the application + */ +public class GeneralPreferenceFragment extends TitlePreferenceFragment { + + private static final String TAG = "GeneralPreferenceFragment"; //$NON-NLS-1$ + + private static final boolean DEBUG = false; + + private CheckBoxPreference mCaseSensitiveSort; + private ListPreference mFreeDiskSpaceWarningLevel; + private CheckBoxPreference mComputeFolderStatistics; +// private CheckBoxPreference mUseFlinger; + private ListPreference mAccessMode; + private CheckBoxPreference mDebugTraces; + + /** + * @hide + */ + boolean mLoaded = false; + + private final OnPreferenceChangeListener mOnChangeListener = + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + boolean ret = true; + + String key = preference.getKey(); + if (DEBUG) { + Log.d(TAG, + String.format("New value for %s: %s", //$NON-NLS-1$ + key, + String.valueOf(newValue))); + } + + // Disk usage warning level + if (FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL. + getId().compareTo(key) == 0) { + String value = (String)newValue; + preference.setSummary( + getResources().getString( + R.string.pref_disk_usage_warning_level_summary, value)); + } + + // Access mode + else if (FileManagerSettings.SETTINGS_ACCESS_MODE.getId().compareTo(key) == 0) { + Activity activity = GeneralPreferenceFragment.this.getActivity(); + + String value = (String)newValue; + AccessMode oldMode = FileManagerApplication.getAccessMode(); + AccessMode newMode = AccessMode.fromId(value); + if (oldMode.compareTo(newMode) != 0) { + // The mode was changes. Change the console + if (newMode.compareTo(AccessMode.ROOT) == 0) { + if (!ConsoleBuilder.changeToPrivilegedConsole( + activity.getApplicationContext())) { + value = String.valueOf(oldMode.ordinal()); + ret = false; + } + } else { + if (!ConsoleBuilder.changeToNonPrivilegedConsole( + activity.getApplicationContext())) { + value = String.valueOf(oldMode.ordinal()); + ret = false; + } + } + } + + int valueId = Integer.valueOf(value).intValue(); + String[] summary = getResources().getStringArray( + R.array.access_mode_summaries); + preference.setSummary(summary[valueId]); + } + + // Notify the change (only if fragment is loaded. Default values are loaded + // while not in loaded mode) + if (GeneralPreferenceFragment.this.mLoaded && ret) { + Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); + intent.putExtra( + FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); + getActivity().sendBroadcast(intent); + } + + return ret; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_general); + + // Case sensitive sort + this.mCaseSensitiveSort = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_CASE_SENSITIVE_SORT.getId()); + this.mCaseSensitiveSort.setOnPreferenceChangeListener(this.mOnChangeListener); + + //Disk usage warning level + this.mFreeDiskSpaceWarningLevel = + (ListPreference)findPreference( + FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()); + this.mFreeDiskSpaceWarningLevel.setOnPreferenceChangeListener(this.mOnChangeListener); + String defaultValue = ((String)FileManagerSettings. + SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue()); + String value = Preferences.getSharedPreferences().getString( + FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(), + defaultValue); + this.mOnChangeListener.onPreferenceChange(this.mFreeDiskSpaceWarningLevel, value); + + // Compute folder statistics + this.mComputeFolderStatistics = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_COMPUTE_FOLDER_STATISTICS.getId()); + this.mComputeFolderStatistics.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Use flinger +// this.mUseFlinger = +// (CheckBoxPreference)findPreference( +// FileManagerSettings.SETTINGS_USE_FLINGER.getId()); +// this.mUseFlinger.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Access mode + this.mAccessMode = + (ListPreference)findPreference( + FileManagerSettings.SETTINGS_ACCESS_MODE.getId()); + this.mAccessMode.setOnPreferenceChangeListener(this.mOnChangeListener); + defaultValue = ((ObjectStringIdentifier)FileManagerSettings. + SETTINGS_ACCESS_MODE.getDefaultValue()).getId(); + value = Preferences.getSharedPreferences().getString( + FileManagerSettings.SETTINGS_ACCESS_MODE.getId(), + defaultValue); + this.mOnChangeListener.onPreferenceChange(this.mAccessMode, value); + // If device is not rooted, this setting cannot be changed + this.mAccessMode.setEnabled(FileManagerApplication.isDeviceRooted()); + + // Capture Debug traces + this.mDebugTraces = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_SHOW_TRACES.getId()); + this.mDebugTraces.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Loaded + this.mLoaded = true; + } + + /** + * {@inheritDoc} + */ + @Override + public CharSequence getTitle() { + return getString(R.string.pref_general); + } +} diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/SearchPreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/SearchPreferenceFragment.java new file mode 100644 index 00000000..d826f154 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/SearchPreferenceFragment.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.provider.SearchRecentSuggestions; +import android.util.Log; +import android.widget.Toast; + +import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.preferences.FileManagerSettings; +import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier; +import com.cyanogenmod.filemanager.preferences.Preferences; +import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider; +import com.cyanogenmod.filemanager.util.DialogHelper; + +/** + * A class that manages the search options + */ +public class SearchPreferenceFragment extends TitlePreferenceFragment { + + private static final String TAG = "SearchPreferenceFragment"; //$NON-NLS-1$ + + private static final boolean DEBUG = false; + + // Internal keys + private static final String REMOVE_SEARCH_TERMS_KEY = + "cm_filemanager_remove_saved_search_terms"; //$NON-NLS-1$ + + private CheckBoxPreference mHighlightTerms; + private CheckBoxPreference mShowRelevanceWidget; + private ListPreference mSortSearchResultMode; + private CheckBoxPreference mSaveSearchTerms; + private Preference mRemoveSearchTerms; + + /** + * @hide + */ + boolean mLoaded = false; + + private final OnPreferenceChangeListener mOnChangeListener = + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String key = preference.getKey(); + if (DEBUG) { + Log.d(TAG, + String.format("New value for %s: %s", //$NON-NLS-1$ + key, + String.valueOf(newValue))); + } + + // Saved search terms + if (preference.getKey().compareTo( + FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId()) == 0) { + if (!((Boolean)newValue).booleanValue()) { + // Remove search terms if saved search terms + // is not active by the user + clearRecentSearchTerms(); + } + + // Sort search result mode + } else if (FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE. + getId().compareTo(key) == 0) { + int value = Integer.valueOf((String)newValue).intValue(); + String[] summary = getResources().getStringArray( + R.array.sort_search_results_mode_labels); + preference.setSummary(summary[value]); + } + + // Notify the change (only if fragment is loaded. Default values are loaded + // while not in loaded mode) + if (SearchPreferenceFragment.this.mLoaded) { + Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); + intent.putExtra( + FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); + getActivity().sendBroadcast(intent); + } + + return true; + } + }; + + private final OnPreferenceClickListener mOnClickListener = + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (preference.getKey().compareTo(REMOVE_SEARCH_TERMS_KEY) == 0) { + // Remove search terms + clearRecentSearchTerms(); + + // Advise the user + DialogHelper.showToast( + getActivity(), + getActivity().getString(R.string.pref_remove_saved_search_terms_msg), + Toast.LENGTH_SHORT); + } + return false; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + this.mLoaded = false; + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_search); + + // Highlight terms + this.mHighlightTerms = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_HIGHLIGHT_TERMS.getId()); + this.mHighlightTerms.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Relevance widget + this.mShowRelevanceWidget = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_SHOW_RELEVANCE_WIDGET.getId()); + this.mShowRelevanceWidget.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Sort search result mode + this.mSortSearchResultMode = + (ListPreference)findPreference( + FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId()); + this.mSortSearchResultMode.setOnPreferenceChangeListener(this.mOnChangeListener); + String defaultValue = ((ObjectStringIdentifier)FileManagerSettings. + SETTINGS_SORT_SEARCH_RESULTS_MODE.getDefaultValue()).getId(); + String value = Preferences.getSharedPreferences().getString( + FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId(), + defaultValue); + this.mOnChangeListener.onPreferenceChange(this.mSortSearchResultMode, value); + + // Saved search terms + this.mSaveSearchTerms = + (CheckBoxPreference)findPreference( + FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId()); + this.mSaveSearchTerms.setOnPreferenceChangeListener(this.mOnChangeListener); + + // Remove search terms + this.mRemoveSearchTerms = findPreference(REMOVE_SEARCH_TERMS_KEY); + this.mRemoveSearchTerms.setOnPreferenceClickListener(this.mOnClickListener); + + // Loaded + this.mLoaded = true; + } + + /** + * Method that removes the recent suggestions on search activity + * @hide + */ + void clearRecentSearchTerms() { + SearchRecentSuggestions suggestions = + new SearchRecentSuggestions(getActivity(), + RecentSearchesContentProvider.AUTHORITY, + RecentSearchesContentProvider.MODE); + suggestions.clearHistory(); + Preferences.setLastSearch(null); + } + + /** + * {@inheritDoc} + */ + @Override + public CharSequence getTitle() { + return getString(R.string.pref_search); + } +} diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java b/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java index 1119756c..fd4bbe1e 100644 --- a/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java +++ b/src/com/cyanogenmod/filemanager/activities/preferences/SettingsPreferences.java @@ -17,39 +17,24 @@ package com.cyanogenmod.filemanager.activities.preferences; import android.app.ActionBar; -import android.app.Activity; +import android.app.Fragment; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceActivity; -import android.preference.PreferenceFragment; -import android.provider.SearchRecentSuggestions; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.TextView; -import android.widget.Toast; -import com.cyanogenmod.filemanager.FileManagerApplication; import com.cyanogenmod.filemanager.R; import com.cyanogenmod.filemanager.activities.ChangeLogActivity; -import com.cyanogenmod.filemanager.console.ConsoleBuilder; -import com.cyanogenmod.filemanager.preferences.AccessMode; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; -import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier; -import com.cyanogenmod.filemanager.preferences.Preferences; -import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider; import com.cyanogenmod.filemanager.ui.ThemeManager; import com.cyanogenmod.filemanager.ui.ThemeManager.Theme; -import com.cyanogenmod.filemanager.ui.preferences.ThemeSelectorPreference; -import com.cyanogenmod.filemanager.util.DialogHelper; +import com.cyanogenmod.filemanager.util.AndroidHelper; import java.util.List; @@ -62,6 +47,8 @@ public class SettingsPreferences extends PreferenceActivity { private static final boolean DEBUG = false; + private TextView mTitle; + private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -127,9 +114,9 @@ public class SettingsPreferences extends PreferenceActivity { ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); getActionBar().setDisplayHomeAsUpEnabled(true); View customTitle = getLayoutInflater().inflate(R.layout.simple_customtitle, null, false); - TextView title = (TextView)customTitle.findViewById(R.id.customtitle_title); - title.setText(R.string.pref); - title.setContentDescription(getString(R.string.pref)); + this.mTitle = (TextView)customTitle.findViewById(R.id.customtitle_title); + this.mTitle.setText(R.string.pref); + this.mTitle.setContentDescription(getString(R.string.pref)); getActionBar().setCustomView(customTitle); } @@ -167,422 +154,15 @@ public class SettingsPreferences extends PreferenceActivity { } /** - * A class that manages the commons options of the application - */ - public static class GeneralPreferenceFragment extends PreferenceFragment { - - private CheckBoxPreference mCaseSensitiveSort; - private ListPreference mFreeDiskSpaceWarningLevel; - private CheckBoxPreference mComputeFolderStatistics; -// private CheckBoxPreference mUseFlinger; - private ListPreference mAccessMode; - private CheckBoxPreference mDebugTraces; - - /** - * @hide - */ - boolean mLoaded = false; - - private final OnPreferenceChangeListener mOnChangeListener = - new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - boolean ret = true; - - String key = preference.getKey(); - if (DEBUG) { - Log.d(TAG, - String.format("New value for %s: %s", //$NON-NLS-1$ - key, - String.valueOf(newValue))); - } - - // Disk usage warning level - if (FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL. - getId().compareTo(key) == 0) { - String value = (String)newValue; - preference.setSummary( - getResources().getString( - R.string.pref_disk_usage_warning_level_summary, value)); - } - - // Access mode - else if (FileManagerSettings.SETTINGS_ACCESS_MODE.getId().compareTo(key) == 0) { - Activity activity = GeneralPreferenceFragment.this.getActivity(); - - String value = (String)newValue; - AccessMode oldMode = FileManagerApplication.getAccessMode(); - AccessMode newMode = AccessMode.fromId(value); - if (oldMode.compareTo(newMode) != 0) { - // The mode was changes. Change the console - if (newMode.compareTo(AccessMode.ROOT) == 0) { - if (!ConsoleBuilder.changeToPrivilegedConsole( - activity.getApplicationContext())) { - value = String.valueOf(oldMode.ordinal()); - ret = false; - } - } else { - if (!ConsoleBuilder.changeToNonPrivilegedConsole( - activity.getApplicationContext())) { - value = String.valueOf(oldMode.ordinal()); - ret = false; - } - } - } - - int valueId = Integer.valueOf(value).intValue(); - String[] summary = getResources().getStringArray( - R.array.access_mode_summaries); - preference.setSummary(summary[valueId]); - } - - // Notify the change (only if fragment is loaded. Default values are loaded - // while not in loaded mode) - if (GeneralPreferenceFragment.this.mLoaded && ret) { - Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); - intent.putExtra( - FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); - getActivity().sendBroadcast(intent); - } - - return ret; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); - getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_general); - - // Case sensitive sort - this.mCaseSensitiveSort = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_CASE_SENSITIVE_SORT.getId()); - this.mCaseSensitiveSort.setOnPreferenceChangeListener(this.mOnChangeListener); - - //Disk usage warning level - this.mFreeDiskSpaceWarningLevel = - (ListPreference)findPreference( - FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()); - this.mFreeDiskSpaceWarningLevel.setOnPreferenceChangeListener(this.mOnChangeListener); - String defaultValue = ((String)FileManagerSettings. - SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue()); - String value = Preferences.getSharedPreferences().getString( - FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(), - defaultValue); - this.mOnChangeListener.onPreferenceChange(this.mFreeDiskSpaceWarningLevel, value); - - // Compute folder statistics - this.mComputeFolderStatistics = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_COMPUTE_FOLDER_STATISTICS.getId()); - this.mComputeFolderStatistics.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Use flinger -// this.mUseFlinger = -// (CheckBoxPreference)findPreference( -// FileManagerSettings.SETTINGS_USE_FLINGER.getId()); -// this.mUseFlinger.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Access mode - this.mAccessMode = - (ListPreference)findPreference( - FileManagerSettings.SETTINGS_ACCESS_MODE.getId()); - this.mAccessMode.setOnPreferenceChangeListener(this.mOnChangeListener); - defaultValue = ((ObjectStringIdentifier)FileManagerSettings. - SETTINGS_ACCESS_MODE.getDefaultValue()).getId(); - value = Preferences.getSharedPreferences().getString( - FileManagerSettings.SETTINGS_ACCESS_MODE.getId(), - defaultValue); - this.mOnChangeListener.onPreferenceChange(this.mAccessMode, value); - // If device is not rooted, this setting cannot be changed - this.mAccessMode.setEnabled(FileManagerApplication.isDeviceRooted()); - - // Capture Debug traces - this.mDebugTraces = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_SHOW_TRACES.getId()); - this.mDebugTraces.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Loaded - this.mLoaded = true; - } - } - - /** - * A class that manages the search options - */ - public static class SearchPreferenceFragment extends PreferenceFragment { - - // Internal keys - private static final String REMOVE_SEARCH_TERMS_KEY = - "cm_filemanager_remove_saved_search_terms"; //$NON-NLS-1$ - - private CheckBoxPreference mHighlightTerms; - private CheckBoxPreference mShowRelevanceWidget; - private ListPreference mSortSearchResultMode; - private CheckBoxPreference mSaveSearchTerms; - private Preference mRemoveSearchTerms; - - /** - * @hide - */ - boolean mLoaded = false; - - private final OnPreferenceChangeListener mOnChangeListener = - new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - if (DEBUG) { - Log.d(TAG, - String.format("New value for %s: %s", //$NON-NLS-1$ - key, - String.valueOf(newValue))); - } - - // Saved search terms - if (preference.getKey().compareTo( - FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId()) == 0) { - if (!((Boolean)newValue).booleanValue()) { - // Remove search terms if saved search terms - // is not active by the user - clearRecentSearchTerms(); - } - - // Sort search result mode - } else if (FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE. - getId().compareTo(key) == 0) { - int value = Integer.valueOf((String)newValue).intValue(); - String[] summary = getResources().getStringArray( - R.array.sort_search_results_mode_labels); - preference.setSummary(summary[value]); - } - - // Notify the change (only if fragment is loaded. Default values are loaded - // while not in loaded mode) - if (SearchPreferenceFragment.this.mLoaded) { - Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); - intent.putExtra( - FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); - getActivity().sendBroadcast(intent); - } - - return true; - } - }; - - private final OnPreferenceClickListener mOnClickListener = - new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - if (preference.getKey().compareTo(REMOVE_SEARCH_TERMS_KEY) == 0) { - // Remove search terms - clearRecentSearchTerms(); - - // Advise the user - DialogHelper.showToast( - getActivity(), - getActivity().getString(R.string.pref_remove_saved_search_terms_msg), - Toast.LENGTH_SHORT); - } - return false; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); - getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); - this.mLoaded = false; - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_search); - - // Highlight terms - this.mHighlightTerms = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_HIGHLIGHT_TERMS.getId()); - this.mHighlightTerms.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Relevance widget - this.mShowRelevanceWidget = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_SHOW_RELEVANCE_WIDGET.getId()); - this.mShowRelevanceWidget.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Sort search result mode - this.mSortSearchResultMode = - (ListPreference)findPreference( - FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId()); - this.mSortSearchResultMode.setOnPreferenceChangeListener(this.mOnChangeListener); - String defaultValue = ((ObjectStringIdentifier)FileManagerSettings. - SETTINGS_SORT_SEARCH_RESULTS_MODE.getDefaultValue()).getId(); - String value = Preferences.getSharedPreferences().getString( - FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId(), - defaultValue); - this.mOnChangeListener.onPreferenceChange(this.mSortSearchResultMode, value); - - // Saved search terms - this.mSaveSearchTerms = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId()); - this.mSaveSearchTerms.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Remove search terms - this.mRemoveSearchTerms = findPreference(REMOVE_SEARCH_TERMS_KEY); - this.mRemoveSearchTerms.setOnPreferenceClickListener(this.mOnClickListener); - - // Loaded - this.mLoaded = true; - } - - /** - * Method that removes the recent suggestions on search activity - * @hide - */ - void clearRecentSearchTerms() { - SearchRecentSuggestions suggestions = - new SearchRecentSuggestions(getActivity(), - RecentSearchesContentProvider.AUTHORITY, - RecentSearchesContentProvider.MODE); - suggestions.clearHistory(); - Preferences.setLastSearch(null); - } - } - - /** - * A class that manages the editor options - */ - public static class EditorPreferenceFragment extends PreferenceFragment { - - private CheckBoxPreference mWordWrap; - - /** - * @hide - */ - boolean mLoaded = false; - - private final OnPreferenceChangeListener mOnChangeListener = - new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - boolean ret = true; - - String key = preference.getKey(); - if (DEBUG) { - Log.d(TAG, - String.format("New value for %s: %s", //$NON-NLS-1$ - key, - String.valueOf(newValue))); - } - - // Notify the change (only if fragment is loaded. Default values are loaded - // while not in loaded mode) - if (EditorPreferenceFragment.this.mLoaded && ret) { - Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED); - intent.putExtra( - FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey()); - getActivity().sendBroadcast(intent); - } - - return ret; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); - getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); - this.mLoaded = false; - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_editor); - - // WordWrap - this.mWordWrap = - (CheckBoxPreference)findPreference( - FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId()); - this.mWordWrap.setOnPreferenceChangeListener(this.mOnChangeListener); - - // Loaded - this.mLoaded = true; - } - } - - /** - * A class that manages the theme selection + * {@inheritDoc} */ - public static class ThemesPreferenceFragment extends PreferenceFragment { - - private ThemeSelectorPreference mThemeSelector; - - private final OnPreferenceChangeListener mOnChangeListener = - new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - if (DEBUG) { - Log.d(TAG, - String.format("New value for %s: %s", //$NON-NLS-1$ - key, - String.valueOf(newValue))); - } - - // Notify to all activities that the theme has changed - Intent intent = new Intent(FileManagerSettings.INTENT_THEME_CHANGED); - intent.putExtra(FileManagerSettings.EXTRA_THEME_ID, (String)newValue); - getActivity().sendBroadcast(intent); - - //Wait for allow activities to apply the theme, prior to finish settings - try { - Thread.sleep(250L); - } catch (Throwable e) {/**NON BLOCK**/} - getActivity().finish(); - return true; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); - getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE); - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_themes); - - // Theme selector - this.mThemeSelector = - (ThemeSelectorPreference)findPreference( - FileManagerSettings.SETTINGS_THEME.getId()); - this.mThemeSelector.setOnPreferenceChangeListener(this.mOnChangeListener); + @Override + public void onAttachFragment(Fragment fragment) { + super.onAttachFragment(fragment); + if (!AndroidHelper.isTablet(this) && fragment instanceof TitlePreferenceFragment) { + this.mTitle.setText(((TitlePreferenceFragment)fragment).getTitle()); + } else { + this.mTitle.setText(R.string.pref); } } diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/ThemesPreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/ThemesPreferenceFragment.java new file mode 100644 index 00000000..ad0a99fc --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/ThemesPreferenceFragment.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.util.Log; + +import com.cyanogenmod.filemanager.R; +import com.cyanogenmod.filemanager.preferences.FileManagerSettings; +import com.cyanogenmod.filemanager.preferences.Preferences; +import com.cyanogenmod.filemanager.ui.preferences.ThemeSelectorPreference; + +/** + * A class that manages the theme selection + */ +public class ThemesPreferenceFragment extends TitlePreferenceFragment { + + private static final String TAG = "ThemesPreferenceFragment"; //$NON-NLS-1$ + + private static final boolean DEBUG = false; + + private ThemeSelectorPreference mThemeSelector; + + private final OnPreferenceChangeListener mOnChangeListener = + new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String key = preference.getKey(); + if (DEBUG) { + Log.d(TAG, + String.format("New value for %s: %s", //$NON-NLS-1$ + key, + String.valueOf(newValue))); + } + + // Notify to all activities that the theme has changed + Intent intent = new Intent(FileManagerSettings.INTENT_THEME_CHANGED); + intent.putExtra(FileManagerSettings.EXTRA_THEME_ID, (String)newValue); + getActivity().sendBroadcast(intent); + + //Wait for allow activities to apply the theme, prior to finish settings + try { + Thread.sleep(250L); + } catch (Throwable e) {/**NON BLOCK**/} + getActivity().finish(); + return true; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_themes); + + // Theme selector + this.mThemeSelector = + (ThemeSelectorPreference)findPreference( + FileManagerSettings.SETTINGS_THEME.getId()); + this.mThemeSelector.setOnPreferenceChangeListener(this.mOnChangeListener); + } + + /** + * {@inheritDoc} + */ + @Override + public CharSequence getTitle() { + return getString(R.string.pref_themes); + } +} diff --git a/src/com/cyanogenmod/filemanager/activities/preferences/TitlePreferenceFragment.java b/src/com/cyanogenmod/filemanager/activities/preferences/TitlePreferenceFragment.java new file mode 100644 index 00000000..e835dea1 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/activities/preferences/TitlePreferenceFragment.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.activities.preferences; + +import android.preference.PreferenceFragment; + +/** + * The base class of all preference fragments of the filemanager app + */ +public abstract class TitlePreferenceFragment extends PreferenceFragment { + /** + * Method that returns the title of the preference fragment + * + * @return CharSequence The title of the fragment + */ + public abstract CharSequence getTitle(); +} diff --git a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java index c1818aee..6f66190d 100644 --- a/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java +++ b/src/com/cyanogenmod/filemanager/preferences/FileManagerSettings.java @@ -130,12 +130,46 @@ public enum FileManagerSettings { SETTINGS_SHOW_TRACES("cm_filemanager_show_debug_traces", Boolean.FALSE), //$NON-NLS-1$ /** + * When to editor should display suggestions + * @hide + */ + SETTINGS_EDITOR_NO_SUGGESTIONS( + "cm_filemanager_editor_no_suggestions", Boolean.FALSE), //$NON-NLS-1$ + + /** * When to editor should use word wrap * @hide */ SETTINGS_EDITOR_WORD_WRAP("cm_filemanager_editor_word_wrap", Boolean.TRUE), //$NON-NLS-1$ /** + * When to editor should open a binary file in a hex viewer + * @hide + */ + SETTINGS_EDITOR_HEXDUMP("cm_filemanager_editor_hexdump", Boolean.TRUE), //$NON-NLS-1$ + + /** + * When to editor should use the syntax highlight + * @hide + */ + SETTINGS_EDITOR_SYNTAX_HIGHLIGHT( + "cm_filemanager_editor_syntax_highlight", Boolean.TRUE), //$NON-NLS-1$ + + /** + * When to editor should use the default color scheme of the theme for syntax highlight + * @hide + */ + SETTINGS_EDITOR_SH_USE_THEME_DEFAULT( + "cm_filemanager_editor_sh_use_theme_default", Boolean.TRUE), //$NON-NLS-1$ + + /** + * When to editor should use the default color scheme of the theme for syntax highlight + * @hide + */ + SETTINGS_EDITOR_SH_COLOR_SCHEME( + "cm_filemanager_editor_sh_color_scheme", ""), //$NON-NLS-1$ //$NON-NLS-2$ + + /** * The current theme to use in the app * @hide */ diff --git a/src/com/cyanogenmod/filemanager/preferences/Preferences.java b/src/com/cyanogenmod/filemanager/preferences/Preferences.java index bbd218ae..171c63d4 100644 --- a/src/com/cyanogenmod/filemanager/preferences/Preferences.java +++ b/src/com/cyanogenmod/filemanager/preferences/Preferences.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; /** * A helper class for access and manage the preferences of the application. @@ -168,6 +169,7 @@ public final class Preferences { * @throws InvalidClassException If the value of a preference is not of the * type of the preference */ + @SuppressWarnings("unchecked") private static void savePreferences( Map<FileManagerSettings, Object> prefs, boolean noSaveIfExists, boolean applied) throws InvalidClassException { @@ -190,6 +192,8 @@ public final class Preferences { editor.putBoolean(pref.getId(), ((Boolean)value).booleanValue()); } else if (value instanceof String && pref.getDefaultValue() instanceof String) { editor.putString(pref.getId(), (String)value); + } else if (value instanceof Set && pref.getDefaultValue() instanceof Set) { + editor.putStringSet(pref.getId(), (Set<String>)value); } else if (value instanceof ObjectIdentifier && pref.getDefaultValue() instanceof ObjectIdentifier) { editor.putInt(pref.getId(), ((ObjectIdentifier)value).getId()); diff --git a/src/com/cyanogenmod/filemanager/ui/preferences/ColorPickerPreference.java b/src/com/cyanogenmod/filemanager/ui/preferences/ColorPickerPreference.java new file mode 100644 index 00000000..22b5b261 --- /dev/null +++ b/src/com/cyanogenmod/filemanager/ui/preferences/ColorPickerPreference.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyanogenmod.filemanager.ui.preferences; + +import afzkl.development.mColorPicker.views.ColorDialogView; +import afzkl.development.mColorPicker.views.ColorPanelView; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; + +import com.cyanogenmod.filemanager.R; + +/** + * A {@link Preference} that allow to select/pick a color in a new window dialog. + */ +public class ColorPickerPreference extends DialogPreference { + + private ColorPanelView mColorPicker; + private int mColor; + + private ColorDialogView mColorDlg; + + /** + * Constructor of <code>ColorPickerPreference</code> + * + * @param context The current context + */ + public ColorPickerPreference(Context context) { + this(context, null); + } + + /** + * Constructor of <code>ColorPickerPreference</code> + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the preference. + */ + public ColorPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setWidgetLayoutResource(R.layout.color_picker_pref_item); + } + + /** + * Returns the color of the picker. + * + * @return The color of the picker. + */ + public int getColor() { + return this.mColor; + } + + /** + * Sets the color of the picker and saves it to the {@link SharedPreferences}. + * + * @param color The new color. + */ + public void setColor(int color) { + // Always persist/notify the first time; don't assume the field's default of false. + final boolean changed = this.mColor != color; + if (changed) { + this.mColor = color; + // when called from onSetInitialValue the view is still not set + if (this.mColorPicker != null) { + this.mColorPicker.setColor(color); + } + persistInt(color); + if (changed) { + notifyDependencyChange(shouldDisableDependents()); + notifyChanged(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return Integer.valueOf(a.getColor(index, 0)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + setColor(restoreValue ? getPersistedInt(0) : ((Integer)defaultValue).intValue()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPrepareDialogBuilder(Builder builder) { + super.onPrepareDialogBuilder(builder); + + // Configure the dialog + this.mColorDlg = new ColorDialogView(getContext()); + this.mColorDlg.setColor(this.mColor); + this.mColorDlg.showAlphaSlider(true); + this.mColorDlg.setAlphaSliderText( + getContext().getString(R.string.color_picker_alpha_slider_text)); + this.mColorDlg.setCurrentColorText( + getContext().getString(R.string.color_picker_current_text)); + this.mColorDlg.setNewColorText( + getContext().getString(R.string.color_picker_new_text)); + this.mColorDlg.setColorLabelText( + getContext().getString(R.string.color_picker_color)); + builder.setView(this.mColorDlg); + + // The color is selected by the user and confirmed by clicking ok + builder.setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + @SuppressWarnings("synthetic-access") + public void onClick(DialogInterface dialog, int which) { + int color = ColorPickerPreference.this.mColorDlg.getColor(); + if (callChangeListener(Integer.valueOf(color))) { + setColor(color); + } + dialog.dismiss(); + } + }); + } + + + /** + * {@inheritDoc} + */ + @Override + protected void onBindView(View view) { + super.onBindView(view); + View v = view.findViewById(R.id.color_picker); + if (v != null && v instanceof ColorPanelView) { + this.mColorPicker = (ColorPanelView)v; + this.mColorPicker.setColor(this.mColor); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.color = getColor(); + return myState; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + setColor(myState.color); + } + + /** + * A class for managing the instance state of a {@link ColorPickerPreference}. + */ + static class SavedState extends BaseSavedState { + int color; + + /** + * Constructor of <code>SavedState</code> + * + * @param source The source + */ + public SavedState(Parcel source) { + super(source); + this.color = source.readInt(); + } + + /** + * Constructor of <code>SavedState</code> + * + * @param superState The parcelable state + */ + public SavedState(Parcelable superState) { + super(superState); + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(this.color); + } + + /** + * A class that generates instances of the <code>SavedState</code> class from a Parcel. + */ + @SuppressWarnings("hiding") + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + + /** + * {@inheritDoc} + */ + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + /** + * {@inheritDoc} + */ + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/InlineAutocompleteTextView.java b/src/com/cyanogenmod/filemanager/ui/widgets/InlineAutocompleteTextView.java index 308be39f..1613f8a8 100644 --- a/src/com/cyanogenmod/filemanager/ui/widgets/InlineAutocompleteTextView.java +++ b/src/com/cyanogenmod/filemanager/ui/widgets/InlineAutocompleteTextView.java @@ -385,7 +385,7 @@ public class InlineAutocompleteTextView extends RelativeLayout private static List<String> filter(List<String> data, String current) { List<String> filter = new ArrayList<String>(data); int size = filter.size(); - for (int i=size-1; i>=0; i--) { + for (int i = size-1; i >= 0; i--) { String s = filter.get(i); if (!s.startsWith(current)) { filter.remove(i); diff --git a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java index 9695760d..11570b43 100644 --- a/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java +++ b/src/com/cyanogenmod/filemanager/ui/widgets/NavigationView.java @@ -870,7 +870,7 @@ public class NavigationView extends RelativeLayout implements @Override @SuppressWarnings("unqualified-field-access") protected void onPostExecute(Boolean result) { - if (!result.booleanValue()){ + if (!result.booleanValue()) { return; } onPostExecuteTask( diff --git a/src/com/cyanogenmod/filemanager/util/CommandHelper.java b/src/com/cyanogenmod/filemanager/util/CommandHelper.java index e6620601..8dddbcba 100644 --- a/src/com/cyanogenmod/filemanager/util/CommandHelper.java +++ b/src/com/cyanogenmod/filemanager/util/CommandHelper.java @@ -1297,7 +1297,7 @@ public final class CommandHelper { newCreator(). createDeleteFileExecutable(compressOutFile); writableExecute(context, executable3, c, true); - if(executable3.getResult().booleanValue()){ + if (executable3.getResult().booleanValue()) { //- Compress execute(context, executable1, c); return executable1; diff --git a/src/com/cyanogenmod/filemanager/util/FileHelper.java b/src/com/cyanogenmod/filemanager/util/FileHelper.java index d1515047..45c8acb5 100644 --- a/src/com/cyanogenmod/filemanager/util/FileHelper.java +++ b/src/com/cyanogenmod/filemanager/util/FileHelper.java @@ -1126,22 +1126,22 @@ public final class FileHelper { throw new InsufficientPermissionsException(executable); } // Check others - if (permissions.getOthers().isRead() ){ + if (permissions.getOthers().isRead()) { return; } // Check user - if (user.getId() == identity.getUser().getId() && permissions.getUser().isRead() ){ + if (user.getId() == identity.getUser().getId() && permissions.getUser().isRead()) { return; } // Check group - if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isRead() ){ + if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isRead()) { return; } // Check groups int cc = groups.size(); for (int i = 0; i < cc; i++) { Group g = groups.get(i); - if (group.getId() == g.getId() && permissions.getGroup().isRead() ){ + if (group.getId() == g.getId() && permissions.getGroup().isRead()) { return; } } @@ -1181,22 +1181,22 @@ public final class FileHelper { throw new InsufficientPermissionsException(executable); } // Check others - if (permissions.getOthers().isWrite() ){ + if (permissions.getOthers().isWrite()) { return; } // Check user - if (user.getId() == identity.getUser().getId() && permissions.getUser().isWrite() ){ + if (user.getId() == identity.getUser().getId() && permissions.getUser().isWrite()) { return; } // Check group - if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isWrite() ){ + if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isWrite()) { return; } // Check groups int cc = groups.size(); for (int i = 0; i < cc; i++) { Group g = groups.get(i); - if (group.getId() == g.getId() && permissions.getGroup().isWrite() ){ + if (group.getId() == g.getId() && permissions.getGroup().isWrite()) { return; } } @@ -1236,22 +1236,22 @@ public final class FileHelper { throw new InsufficientPermissionsException(executable); } // Check others - if (permissions.getOthers().isExecute() ){ + if (permissions.getOthers().isExecute()) { return; } // Check user - if (user.getId() == identity.getUser().getId() && permissions.getUser().isExecute() ){ + if (user.getId() == identity.getUser().getId() && permissions.getUser().isExecute()) { return; } // Check group - if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isExecute() ){ + if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isExecute()) { return; } // Check groups int cc = groups.size(); for (int i = 0; i < cc; i++) { Group g = groups.get(i); - if (group.getId() == g.getId() && permissions.getGroup().isExecute() ){ + if (group.getId() == g.getId() && permissions.getGroup().isExecute()) { return; } } diff --git a/themes/res/values/dark_theme.xml b/themes/res/values/dark_theme.xml index 70681dfe..1360509b 100644 --- a/themes/res/values/dark_theme.xml +++ b/themes/res/values/dark_theme.xml @@ -19,11 +19,6 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- The dark theme name --> - <string name="dark_theme_name">Dark Theme</string> - <!-- The dark theme description --> - <string name="dark_theme_desc">A dark theme for CyanogenMod File Manager.</string> - <!-- The base theme used to customize some (non-themeables) widgets like dialogs 2 possibles values: * holo: @android:style/Theme.Holo @@ -174,4 +169,14 @@ <drawable name="dark_fso_type_text_drawable">@null</drawable> <drawable name="dark_fso_type_video_drawable">@null</drawable> --> + + <!-- Syntax Highlight --> + <color name="dark_ash_text_color">#99ffffff</color> + <color name="dark_ash_assignment_color">#99ffffff</color> + <color name="dark_ash_singleline_comment_color">#ff3f7f5f</color> + <color name="dark_ash_multiline_comment_color">#ff7f9fbf</color> + <color name="dark_ash_keyword_color">#ffd2568a</color> + <color name="dark_ash_quoted_string_color">#ff4Ab3b6</color> + <color name="dark_ash_variable_color">#ff91bcf8</color> + </resources> diff --git a/themes/res/values/strings.xml b/themes/res/values/strings.xml new file mode 100644 index 00000000..c28cd5ba --- /dev/null +++ b/themes/res/values/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + + <!-- The dark theme strings --> + <string name="dark_theme_name">Dark Theme</string> + <string name="dark_theme_desc">A dark theme for CyanogenMod File Manager.</string> + +</resources> |