├── .gitignore
├── AndroidManifest.xml
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── graphics
└── screenshot.png
├── pom.xml
├── project.properties
├── res
├── drawable-hdpi
│ ├── ic_close.png
│ ├── ic_gridwichterle.png
│ └── ic_launcher.png
├── drawable-ldpi
│ └── icon.png
├── drawable-mdpi
│ ├── ic_close.png
│ ├── ic_gridwichterle.png
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── ic_close.png
│ ├── ic_gridwichterle.png
│ └── ic_launcher.png
├── drawable
│ ├── bg_btn_black.xml
│ └── bg_btn_white.xml
├── layout
│ ├── activity_main.xml
│ ├── activity_settings.xml
│ ├── dialog_settings_color.xml
│ └── notification_layout.xml
└── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ids.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── themes.xml
└── src
├── com
└── larswerkman
│ └── colorpicker
│ ├── ColorPicker.java
│ ├── OpacityBar.java
│ ├── SVBar.java
│ ├── SaturationBar.java
│ └── ValueBar.java
└── eu
└── inmite
└── android
└── gridwichterle
├── App.java
├── activity
├── MainActivity.java
└── SettingsActivity.java
├── bus
├── BusProvider.java
├── CancelGridBus.java
├── ColorChangeBus.java
├── GridOnOffBus.java
└── ShowSettingsBus.java
├── core
├── Config.java
├── Constants.java
├── NotificationReceiver.java
└── Utils.java
├── dialogs
└── ColorsDialog.java
├── services
└── GridOverlayService.java
└── views
├── DrawView.java
└── GridOverlay.java
/.gitignore:
--------------------------------------------------------------------------------
1 | #Android generated
2 | bin
3 | gen
4 | lint.xml
5 |
6 | #Eclipse
7 | .project
8 | .classpath
9 | .settings
10 | .checkstyle
11 |
12 | #IntelliJ IDEA
13 | .idea
14 | *.iml
15 | *.ipr
16 | *.iws
17 | classes
18 | out
19 | gen-external-apklibs
20 |
21 | #Maven
22 | target
23 | release.properties
24 | pom.xml.*
25 |
26 | #Ant
27 | build.xml
28 | ant.properties
29 | local.properties
30 | proguard.cfg
31 | proguard-project.txt
32 |
33 | #SVN
34 | .svn
35 |
36 | #Other
37 | .DS_Store
38 | tmp
39 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.0.0.
2 |
3 | October 16, 2013
4 |
5 | - initial public release
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2013 Inmite s.r.o. (www.inmite.eu)
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GridWichterle for Android
2 |
3 | This app will show grid overlay over whole system which helps you to verify your excellent app design.
4 |
5 | ## Download:
6 |
7 | [](https://play.google.com/store/apps/details?id=eu.inmite.android.gridwichterle)
8 |
9 |
10 | ## What is the grid good for?
11 | - makes adhering to [Android Design Guidelines - Metrics and Grids](http://developer.android.com/design/style/metrics-grids.html) easy
12 | - your app will look aesthetically pleasing
13 | - designers use grid to create designs, use this app to verify design implementation
14 |
15 | 
16 |
17 | ## How to use:
18 |
19 | - Run the app and see the grid over whole system
20 | - Remove the grid or go to settings via notification
21 |
22 | 
23 |
24 | ## Customization in settings:
25 |
26 | - Grid size in dp (default is 8dp)
27 | - Grid color and transparency
28 | - Fullscreen mode (if the grid should start on the very top of the screen)
29 |
30 | ## How to build the project:
31 |
32 | With Maven:
33 |
34 | - `mvn clean package`
35 |
36 | Or:
37 |
38 | - clone the project and create project from existing sources in IDE
39 |
40 | ## Why 'Wichterle'?
41 |
42 |
43 |
44 | [Otto Wichterle](http://en.wikipedia.org/wiki/Otto_Wichterle) was a famous Czech inventor, best known for his invention of contact lenses.
45 | You can see more details with contact lenses, just like with this app. You won't miss any misaligned views in your app.
46 |
47 | See [**our other Czech personalities**](http://inmite.github.io) who help with [#AndroidDev](https://plus.google.com/s/%23AndroidDev).
48 |
--------------------------------------------------------------------------------
/graphics/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/graphics/screenshot.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
21 | 4.0.0
22 |
23 |
24 | org.sonatype.oss
25 | oss-parent
26 | 7
27 |
28 |
29 | eu.inmite.android.gridwichterle
30 | android-gridwichterle
31 | 1.0.1
32 | apk
33 |
34 | GridWichterle
35 | Grid helps you create pixel perfect application.
36 |
37 |
38 |
39 | Inmite.eu
40 | http://www.inmite.eu
41 |
42 |
43 |
44 |
45 | Apache 2.0
46 | http://www.apache.org/licenses/LICENSE-2.0.txt
47 |
48 |
49 |
50 |
51 |
52 | Michal Matl
53 | michal.matl@inmite.eu
54 |
55 | developer
56 |
57 |
58 |
59 |
60 |
61 | UTF-8
62 |
63 | 1.6
64 | true
65 | 4.1.1.4
66 | 19
67 | r7
68 | false
69 |
70 |
71 | https://github.com/inmite/android-grid-wichterle
72 |
73 |
74 | GitHub
75 | https://github.com/inmite/android-grid-wichterle/issues
76 |
77 |
78 |
79 | scm:git:github.com/inmite/android-grid-wichterle.git
80 |
81 |
82 |
83 |
84 |
85 |
86 | com.google.android
87 | android
88 | ${android.version}
89 | provided
90 |
91 |
92 |
93 | com.google.android
94 | support-v4
95 | ${android.support.version}
96 |
97 |
98 |
99 | com.squareup
100 | otto
101 | 1.3.4
102 |
103 |
104 |
105 | com.jakewharton
106 | butterknife
107 | 3.0.0
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | org.apache.maven.plugins
117 | maven-compiler-plugin
118 | 3.0
119 |
120 | ${java.version}
121 | ${java.version}
122 | true
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | com.jayway.maven.plugins.android.generation2
131 | android-maven-plugin
132 | true
133 | 3.6.0
134 |
135 | ignored
136 |
137 | ${android.platform}
138 |
139 |
140 |
141 |
142 |
143 | org.apache.maven.plugins
144 | maven-javadoc-plugin
145 | 2.9
146 |
147 |
148 |
149 | org.codehaus.mojo
150 | build-helper-maven-plugin
151 | 1.7
152 |
153 |
154 |
155 | com.google.code.maven-replacer-plugin
156 | maven-replacer-plugin
157 | 1.4.0
158 |
159 |
160 |
161 |
162 | org.apache.maven.plugins
163 | maven-release-plugin
164 | 2.4
165 |
166 | forked-path
167 | true
168 |
169 |
170 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library=false
16 |
17 |
18 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-hdpi/ic_close.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_gridwichterle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-hdpi/ic_gridwichterle.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-mdpi/ic_close.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_gridwichterle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-mdpi/ic_gridwichterle.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-xhdpi/ic_close.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_gridwichterle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-xhdpi/ic_gridwichterle.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inmite/android-grid-wichterle/317843b559657ac10a468fe9f001334a22e2039e/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/bg_btn_black.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
13 |
14 |
15 |
16 | -
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/drawable/bg_btn_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
13 |
14 |
15 |
16 | -
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
69 |
70 |
73 |
74 |
75 |
76 |
84 |
85 |
86 |
87 |
88 |
89 |
93 |
94 |
98 |
99 |
106 |
107 |
108 |
109 |
110 |
111 |
116 |
117 |
118 |
119 |
123 |
124 |
125 |
126 |
130 |
131 |
134 |
135 |
138 |
139 |
140 |
141 |
142 |
143 |
147 |
148 |
151 |
152 |
155 |
156 |
157 |
158 |
159 |
160 |
164 |
165 |
166 |
167 |
170 |
171 |
174 |
175 |
178 |
179 |
180 |
181 |
182 |
183 |
186 |
187 |
190 |
191 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/res/layout/dialog_settings_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
10 |
11 |
15 |
16 |
19 |
20 |
24 |
25 |
29 |
30 |
38 |
39 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/res/layout/notification_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
14 |
15 |
16 |
24 |
25 |
31 |
32 |
38 |
39 |
40 |
41 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #111111
6 | #ffffff
7 | #999999
8 | #33B5E5
9 | #343639
10 | #bebebe
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 4dp
3 | 8dp
4 | 16dp
5 | 20dp
6 | 24dp
7 |
8 | 18sp
9 | 16sp
10 | 14sp
11 | 14sp
12 |
13 |
14 |
15 | 124dp
16 | 8dp
17 | 60dp
18 | 54dp
19 | 18dp
20 | 14dp
21 |
22 |
23 | 4dp
24 | 240dp
25 | 6dp
26 | 14dp
27 |
28 |
29 |
--------------------------------------------------------------------------------
/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GridWichterle
5 | Size
6 |
7 | White
8 | Black
9 | Author
10 | Version
11 | Inmite Developers
12 |
13 |
14 |
15 | Please fill grid size!
16 | Square side size must be higher than zero.
17 |
18 |
19 | GridWichterle
20 | Touch for grid settings
21 | Remove Grid
22 |
23 |
24 | Cancel
25 | Set color
26 | Choose color
27 |
28 |
29 | Overlay settings
30 | GRID
31 | ABOUT
32 | Transparency
33 | Close
34 | Save
35 | Full screen
36 | Color
37 | Set color
38 | This app will show grid overlay over whole system which helps you to verify your excellent app design.
39 | The code
40 | %s dp
41 | Send Feedback
42 | Why Wichterle?
43 | Otto Wichterle was a famous Czech inventor, best known for his invention of contact lenses. You can see more details with contact lenses, just like with
44 | this app. You won\'t miss any misaligned views in your app.
45 | Set grid size
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
18 |
19 |
25 |
26 |
31 |
32 |
37 |
38 |
42 |
43 |
50 |
51 |
55 |
56 |
61 |
62 |
69 |
70 |
74 |
75 |
78 |
79 |
83 |
84 |
93 |
94 |
107 |
108 |
113 |
114 |
119 |
120 |
123 |
124 |
125 |
127 |
128 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
15 |
16 |
20 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/src/com/larswerkman/colorpicker/ColorPicker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Lars Werkman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.larswerkman.colorpicker;
18 |
19 | import android.content.Context;
20 | import android.content.res.Resources;
21 | import android.content.res.TypedArray;
22 | import android.graphics.*;
23 | import android.os.Bundle;
24 | import android.os.Parcelable;
25 | import android.util.AttributeSet;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import eu.inmite.android.gridwichterle.R;
29 |
30 | /**
31 | * Displays a holo-themed color picker.
32 | *
33 | *
34 | * Use {@link #getColor()} to retrieve the selected color.
35 | * Use {@link #addSVBar(SVBar)} to add a Saturation/Value Bar.
36 | * Use {@link #addOpacityBar(OpacityBar)} to add a Opacity Bar.
37 | *
38 | */
39 | public class ColorPicker extends View {
40 | /*
41 | * Constants used to save/restore the instance state.
42 | */
43 | private static final String STATE_PARENT = "parent";
44 | private static final String STATE_ANGLE = "angle";
45 | private static final String STATE_OLD_COLOR = "color";
46 |
47 | /**
48 | * Colors to construct the color wheel using {@link SweepGradient}.
49 | *
50 | *
51 | * Note: The algorithm in {@link #normalizeColor(int)} highly depends on
52 | * these exact values. Be aware that {@link #setColor(int)} might break if
53 | * you change this array.
54 | *
55 | */
56 | private static final int[] COLORS = new int[] { 0xFFFF0000, 0xFFFF00FF,
57 | 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 };
58 |
59 | /**
60 | * {@code Paint} instance used to draw the color wheel.
61 | */
62 | private Paint mColorWheelPaint;
63 |
64 | /**
65 | * {@code Paint} instance used to draw the pointer's "halo".
66 | */
67 | private Paint mPointerHaloPaint;
68 |
69 | /**
70 | * {@code Paint} instance used to draw the pointer (the selected color).
71 | */
72 | private Paint mPointerColor;
73 |
74 | /**
75 | * The width of the color wheel thickness.
76 | */
77 | private int mColorWheelThickness;
78 |
79 | /**
80 | * The radius of the color wheel.
81 | */
82 | private int mColorWheelRadius;
83 | private int mPreferredColorWheelRadius;
84 |
85 | /**
86 | * The radius of the center circle inside the color wheel.
87 | */
88 | private int mColorCenterRadius;
89 | private int mPreferredColorCenterRadius;
90 |
91 | /**
92 | * The radius of the halo of the center circle inside the color wheel.
93 | */
94 | private int mColorCenterHaloRadius;
95 | private int mPreferredColorCenterHaloRadius;
96 |
97 | /**
98 | * The radius of the pointer.
99 | */
100 | private int mColorPointerRadius;
101 |
102 | /**
103 | * The radius of the halo of the pointer.
104 | */
105 | private int mColorPointerHaloRadius;
106 |
107 | /**
108 | * The rectangle enclosing the color wheel.
109 | */
110 | private RectF mColorWheelRectangle = new RectF();
111 |
112 | /**
113 | * The rectangle enclosing the center inside the color wheel.
114 | */
115 | private RectF mCenterRectangle = new RectF();
116 |
117 | /**
118 | * {@code true} if the user clicked on the pointer to start the move mode.
119 | * {@code false} once the user stops touching the screen.
120 | *
121 | * @see #onTouchEvent(MotionEvent)
122 | */
123 | private boolean mUserIsMovingPointer = false;
124 |
125 | /**
126 | * The ARGB value of the currently selected color.
127 | */
128 | private int mColor;
129 |
130 | /**
131 | * The ARGB value of the center with the old selected color.
132 | */
133 | private int mCenterOldColor;
134 |
135 | /**
136 | * The ARGB value of the center with the new selected color.
137 | */
138 | private int mCenterNewColor;
139 |
140 | /**
141 | * Number of pixels the origin of this view is moved in X- and Y-direction.
142 | *
143 | *
144 | * We use the center of this (quadratic) View as origin of our internal
145 | * coordinate system. Android uses the upper left corner as origin for the
146 | * View-specific coordinate system. So this is the value we use to translate
147 | * from one coordinate system to the other.
148 | *
149 | *
150 | *
151 | * Note: (Re)calculated in {@link #onMeasure(int, int)}.
152 | *
153 | *
154 | * @see #onDraw(Canvas)
155 | */
156 | private float mTranslationOffset;
157 |
158 | /**
159 | * The pointer's position expressed as angle (in rad).
160 | */
161 | private float mAngle;
162 |
163 | /**
164 | * {@code Paint} instance used to draw the center with the old selected
165 | * color.
166 | */
167 | private Paint mCenterOldPaint;
168 |
169 | /**
170 | * {@code Paint} instance used to draw the center with the new selected
171 | * color.
172 | */
173 | private Paint mCenterNewPaint;
174 |
175 | /**
176 | * {@code Paint} instance used to draw the halo of the center selected
177 | * colors.
178 | */
179 | private Paint mCenterHaloPaint;
180 |
181 | /**
182 | * An array of floats that can be build into a {@code Color}
183 | * Where we can extract the Saturation and Value from.
184 | */
185 | private float[] mHSV = new float[3];
186 |
187 | /**
188 | * {@code SVBar} instance used to control the Saturation/Value bar.
189 | */
190 | private SVBar mSVbar = null;
191 |
192 | /**
193 | * {@code OpacityBar} instance used to control the Opacity bar.
194 | */
195 | private OpacityBar mOpacityBar = null;
196 |
197 | /**
198 | * {@code SaturationBar} instance used to control the Saturation bar.
199 | */
200 | private SaturationBar mSaturationBar = null;
201 |
202 | /**
203 | * {@code ValueBar} instance used to control the Value bar.
204 | */
205 | private ValueBar mValueBar = null;
206 |
207 | /**
208 | * {@code onColorChangedListener} instance of the onColorChangedListener
209 | */
210 | private OnColorChangedListener onColorChangedListener;
211 |
212 | public ColorPicker(Context context) {
213 | super(context);
214 | init(null, 0);
215 | }
216 |
217 | public ColorPicker(Context context, AttributeSet attrs) {
218 | super(context, attrs);
219 | init(attrs, 0);
220 | }
221 |
222 | public ColorPicker(Context context, AttributeSet attrs, int defStyle) {
223 | super(context, attrs, defStyle);
224 | init(attrs, defStyle);
225 | }
226 |
227 | /**
228 | * An interface that is called whenever the color is changed. Currently it
229 | * is always called when the color is changes.
230 | *
231 | * @author lars
232 | *
233 | */
234 | public interface OnColorChangedListener {
235 | public void onColorChanged(int color);
236 | }
237 |
238 | /**
239 | * Set a onColorChangedListener
240 | *
241 | * @param {@code OnColorChangedListener}
242 | */
243 | public void setOnColorChangedListener(OnColorChangedListener listener) {
244 | this.onColorChangedListener = listener;
245 | }
246 |
247 | /**
248 | * Gets the onColorChangedListener
249 | *
250 | * @return {@code OnColorChangedListener}
251 | */
252 | public OnColorChangedListener getOnColorChangedListener() {
253 | return this.onColorChangedListener;
254 | }
255 |
256 | private void init(AttributeSet attrs, int defStyle) {
257 | final TypedArray a = getContext().obtainStyledAttributes(attrs,
258 | R.styleable.ColorPicker, defStyle, 0);
259 | final Resources b = getContext().getResources();
260 |
261 | mColorWheelThickness = a.getDimensionPixelSize(
262 | R.styleable.ColorPicker_color_wheel_thickness,
263 | b.getDimensionPixelSize(R.dimen.color_wheel_thickness));
264 | mColorWheelRadius = a.getDimensionPixelSize(
265 | R.styleable.ColorPicker_color_wheel_radius,
266 | b.getDimensionPixelSize(R.dimen.color_wheel_radius));
267 | mPreferredColorWheelRadius = mColorWheelRadius;
268 | mColorCenterRadius = a.getDimensionPixelSize(
269 | R.styleable.ColorPicker_color_center_radius,
270 | b.getDimensionPixelSize(R.dimen.color_center_radius));
271 | mPreferredColorCenterRadius = mColorCenterRadius;
272 | mColorCenterHaloRadius = a.getDimensionPixelSize(
273 | R.styleable.ColorPicker_color_center_halo_radius,
274 | b.getDimensionPixelSize(R.dimen.color_center_halo_radius));
275 | mPreferredColorCenterHaloRadius = mColorCenterHaloRadius;
276 | mColorPointerRadius = a.getDimensionPixelSize(
277 | R.styleable.ColorPicker_color_pointer_radius,
278 | b.getDimensionPixelSize(R.dimen.color_pointer_radius));
279 | mColorPointerHaloRadius = a.getDimensionPixelSize(
280 | R.styleable.ColorPicker_color_pointer_halo_radius,
281 | b.getDimensionPixelSize(R.dimen.color_pointer_halo_radius));
282 |
283 | a.recycle();
284 |
285 | mAngle = (float) (-Math.PI / 2);
286 |
287 | Shader s = new SweepGradient(0, 0, COLORS, null);
288 |
289 | mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
290 | mColorWheelPaint.setShader(s);
291 | mColorWheelPaint.setStyle(Paint.Style.STROKE);
292 | mColorWheelPaint.setStrokeWidth(mColorWheelThickness);
293 |
294 | mPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
295 | mPointerHaloPaint.setColor(Color.BLACK);
296 | mPointerHaloPaint.setAlpha(0x50);
297 |
298 | mPointerColor = new Paint(Paint.ANTI_ALIAS_FLAG);
299 | mPointerColor.setColor(calculateColor(mAngle));
300 |
301 | mCenterNewPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
302 | mCenterNewPaint.setColor(calculateColor(mAngle));
303 | mCenterNewPaint.setStyle(Paint.Style.FILL);
304 |
305 | mCenterOldPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
306 | mCenterOldPaint.setColor(calculateColor(mAngle));
307 | mCenterOldPaint.setStyle(Paint.Style.FILL);
308 |
309 | mCenterHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
310 | mCenterHaloPaint.setColor(Color.BLACK);
311 | mCenterHaloPaint.setAlpha(0x00);
312 |
313 | }
314 |
315 | @Override
316 | protected void onDraw(Canvas canvas) {
317 | // All of our positions are using our internal coordinate system.
318 | // Instead of translating
319 | // them we let Canvas do the work for us.
320 | canvas.translate(mTranslationOffset, mTranslationOffset);
321 |
322 | // Draw the color wheel.
323 | canvas.drawOval(mColorWheelRectangle, mColorWheelPaint);
324 |
325 | float[] pointerPosition = calculatePointerPosition(mAngle);
326 |
327 | // Draw the pointer's "halo"
328 | canvas.drawCircle(pointerPosition[0], pointerPosition[1],
329 | mColorPointerHaloRadius, mPointerHaloPaint);
330 |
331 | // Draw the pointer (the currently selected color) slightly smaller on
332 | // top.
333 | canvas.drawCircle(pointerPosition[0], pointerPosition[1],
334 | mColorPointerRadius, mPointerColor);
335 |
336 | // Draw the halo of the center colors.
337 | canvas.drawCircle(0, 0, mColorCenterHaloRadius, mCenterHaloPaint);
338 |
339 | // Draw the old selected color in the center.
340 | canvas.drawArc(mCenterRectangle, 90, 180, true, mCenterOldPaint);
341 |
342 | // Draw the new selected color in the center.
343 | canvas.drawArc(mCenterRectangle, 270, 180, true, mCenterNewPaint);
344 | }
345 |
346 | @Override
347 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
348 | final int intrinsicSize = 2 * (mPreferredColorWheelRadius + mColorPointerHaloRadius);
349 |
350 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
351 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
352 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
353 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
354 |
355 | int width;
356 | int height;
357 |
358 | if (widthMode == MeasureSpec.EXACTLY) {
359 | width = widthSize;
360 | } else if (widthMode == MeasureSpec.AT_MOST) {
361 | width = Math.min(intrinsicSize, widthSize);
362 | } else {
363 | width = intrinsicSize;
364 | }
365 |
366 | if (heightMode == MeasureSpec.EXACTLY) {
367 | height = heightSize;
368 | } else if (heightMode == MeasureSpec.AT_MOST) {
369 | height = Math.min(intrinsicSize, heightSize);
370 | } else {
371 | height = intrinsicSize;
372 | }
373 |
374 | int min = Math.min(width, height);
375 | setMeasuredDimension(min, min);
376 | mTranslationOffset = min * 0.5f;
377 |
378 | // fill the rectangle instances.
379 | mColorWheelRadius = min / 2 - mColorWheelThickness - mColorPointerHaloRadius;
380 | mColorWheelRectangle.set(-mColorWheelRadius, -mColorWheelRadius,
381 | mColorWheelRadius, mColorWheelRadius);
382 |
383 | mColorCenterRadius = (int) ((float) mPreferredColorCenterRadius * ((float) mColorWheelRadius / (float) mPreferredColorWheelRadius));
384 | mColorCenterHaloRadius = (int) ((float) mPreferredColorCenterHaloRadius * ((float) mColorWheelRadius / (float) mPreferredColorWheelRadius));
385 | mCenterRectangle.set(-mColorCenterRadius, -mColorCenterRadius,
386 | mColorCenterRadius, mColorCenterRadius);
387 | }
388 |
389 | private int ave(int s, int d, float p) {
390 | return s + java.lang.Math.round(p * (d - s));
391 | }
392 |
393 | /**
394 | * Calculate the color using the supplied angle.
395 | *
396 | * @param angle
397 | * The selected color's position expressed as angle (in rad).
398 | *
399 | * @return The ARGB value of the color on the color wheel at the specified
400 | * angle.
401 | */
402 | private int calculateColor(float angle) {
403 | float unit = (float) (angle / (2 * Math.PI));
404 | if (unit < 0) {
405 | unit += 1;
406 | }
407 |
408 | if (unit <= 0) {
409 | mColor = COLORS[0];
410 | return COLORS[0];
411 | }
412 | if (unit >= 1) {
413 | mColor = COLORS[COLORS.length - 1];
414 | return COLORS[COLORS.length - 1];
415 | }
416 |
417 | float p = unit * (COLORS.length - 1);
418 | int i = (int) p;
419 | p -= i;
420 |
421 | int c0 = COLORS[i];
422 | int c1 = COLORS[i + 1];
423 | int a = ave(Color.alpha(c0), Color.alpha(c1), p);
424 | int r = ave(Color.red(c0), Color.red(c1), p);
425 | int g = ave(Color.green(c0), Color.green(c1), p);
426 | int b = ave(Color.blue(c0), Color.blue(c1), p);
427 |
428 | mColor = Color.argb(a, r, g, b);
429 | return Color.argb(a, r, g, b);
430 | }
431 |
432 | /**
433 | * Get the currently selected color.
434 | *
435 | * @return The ARGB value of the currently selected color.
436 | */
437 | public int getColor() {
438 | return mCenterNewColor;
439 | }
440 |
441 | /**
442 | * Set the color to be highlighted by the pointer. If the
443 | * instances {@code SVBar} and the {@code OpacityBar} aren't null the color
444 | * will also be set to them
445 | *
446 | * @param color
447 | * The RGB value of the color to highlight. If this is not a
448 | * color displayed on the color wheel a very simple algorithm is
449 | * used to map it to the color wheel. The resulting color often
450 | * won't look close to the original color. This is especially
451 | * true for shades of grey. You have been warned!
452 | */
453 | public void setColor(int color) {
454 | mAngle = colorToAngle(color);
455 | mPointerColor.setColor(calculateColor(mAngle));
456 |
457 | // check of the instance isn't null
458 | if (mOpacityBar != null) {
459 | // set the value of the opacity
460 | mOpacityBar.setColor(mColor);
461 | mOpacityBar.setOpacity(Color.alpha(color));
462 | }
463 |
464 | // check if the instance isn't null
465 | if (mSVbar != null) {
466 | // the array mHSV will be filled with the HSV values of the color.
467 | Color.colorToHSV(color, mHSV);
468 | mSVbar.setColor(mColor);
469 |
470 | // because of the design of the Saturation/Value bar,
471 | // we can only use Saturation or Value every time.
472 | // Here will be checked which we shall use.
473 | if (mHSV[1] < mHSV[2]) {
474 | mSVbar.setSaturation(mHSV[1]);
475 | } else { // if (mHSV[1] > mHSV[2]) {
476 | mSVbar.setValue(mHSV[2]);
477 | }
478 | }
479 |
480 | if (mSaturationBar != null) {
481 | Color.colorToHSV(color, mHSV);
482 | mSaturationBar.setColor(mColor);
483 | mSaturationBar.setSaturation(mHSV[1]);
484 | }
485 |
486 | if (mValueBar != null && mSaturationBar == null) {
487 | Color.colorToHSV(color, mHSV);
488 | mValueBar.setColor(mColor);
489 | mValueBar.setValue(mHSV[2]);
490 | } else if (mValueBar != null) {
491 | Color.colorToHSV(color, mHSV);
492 | mValueBar.setValue(mHSV[2]);
493 | }
494 |
495 | invalidate();
496 | }
497 |
498 | /**
499 | * Convert a color to an angle.
500 | *
501 | * @param color
502 | * The RGB value of the color to "find" on the color wheel.
503 | *
504 | * @return The angle (in rad) the "normalized" color is displayed on the
505 | * color wheel.
506 | */
507 | private float colorToAngle(int color) {
508 | float[] colors = new float[3];
509 | Color.colorToHSV(color, colors);
510 |
511 | return (float) Math.toRadians(-colors[0]);
512 | }
513 |
514 | @Override
515 | public boolean onTouchEvent(MotionEvent event) {
516 | getParent().requestDisallowInterceptTouchEvent(true);
517 |
518 | // Convert coordinates to our internal coordinate system
519 | float x = event.getX() - mTranslationOffset;
520 | float y = event.getY() - mTranslationOffset;
521 |
522 | switch (event.getAction()) {
523 | case MotionEvent.ACTION_DOWN:
524 | // Check whether the user pressed on the pointer.
525 | float[] pointerPosition = calculatePointerPosition(mAngle);
526 | if (x >= (pointerPosition[0] - mColorPointerHaloRadius)
527 | && x <= (pointerPosition[0] + mColorPointerHaloRadius)
528 | && y >= (pointerPosition[1] - mColorPointerHaloRadius)
529 | && y <= (pointerPosition[1] + mColorPointerHaloRadius)) {
530 | mUserIsMovingPointer = true;
531 | invalidate();
532 | }
533 | // Check whether the user pressed on the center.
534 | else if (x >= -mColorCenterRadius && x <= mColorCenterRadius
535 | && y >= -mColorCenterRadius && y <= mColorCenterRadius) {
536 | mCenterHaloPaint.setAlpha(0x50);
537 | setColor(getOldCenterColor());
538 | mCenterNewPaint.setColor(getOldCenterColor());
539 | invalidate();
540 | }
541 | // If user did not press pointer or center, report event not handled
542 | else{
543 | getParent().requestDisallowInterceptTouchEvent(false);
544 | return false;
545 | }
546 | break;
547 | case MotionEvent.ACTION_MOVE:
548 | if (mUserIsMovingPointer) {
549 | mAngle = (float) java.lang.Math.atan2(y, x);
550 | mPointerColor.setColor(calculateColor(mAngle));
551 |
552 | setNewCenterColor(mCenterNewColor = calculateColor(mAngle));
553 |
554 | if (mOpacityBar != null) {
555 | mOpacityBar.setColor(mColor);
556 | }
557 |
558 | if (mValueBar != null) {
559 | mValueBar.setColor(mColor);
560 | }
561 |
562 | if (mSaturationBar != null) {
563 | mSaturationBar.setColor(mColor);
564 | }
565 |
566 | if (mSVbar != null) {
567 | mSVbar.setColor(mColor);
568 | }
569 |
570 | invalidate();
571 | }
572 | // If user did not press pointer or center, report event not handled
573 | else{
574 | getParent().requestDisallowInterceptTouchEvent(false);
575 | return false;
576 | }
577 | break;
578 | case MotionEvent.ACTION_UP:
579 | mUserIsMovingPointer = false;
580 | mCenterHaloPaint.setAlpha(0x00);
581 | invalidate();
582 | break;
583 | }
584 | return true;
585 | }
586 |
587 | /**
588 | * Calculate the pointer's coordinates on the color wheel using the supplied
589 | * angle.
590 | *
591 | * @param angle
592 | * The position of the pointer expressed as angle (in rad).
593 | *
594 | * @return The coordinates of the pointer's center in our internal
595 | * coordinate system.
596 | */
597 | private float[] calculatePointerPosition(float angle) {
598 | float x = (float) (mColorWheelRadius * Math.cos(angle));
599 | float y = (float) (mColorWheelRadius * Math.sin(angle));
600 |
601 | return new float[] { x, y };
602 | }
603 |
604 | /**
605 | * Add a Saturation/Value bar to the color wheel.
606 | *
607 | * @param bar
608 | * The instance of the Saturation/Value bar.
609 | */
610 | public void addSVBar(SVBar bar) {
611 | mSVbar = bar;
612 | // Give an instance of the color picker to the Saturation/Value bar.
613 | mSVbar.setColorPicker(this);
614 | mSVbar.setColor(mColor);
615 | }
616 |
617 | /**
618 | * Add a Opacity bar to the color wheel.
619 | *
620 | * @param bar
621 | * The instance of the Opacity bar.
622 | */
623 | public void addOpacityBar(OpacityBar bar) {
624 | mOpacityBar = bar;
625 | // Give an instance of the color picker to the Opacity bar.
626 | mOpacityBar.setColorPicker(this);
627 | mOpacityBar.setColor(mColor);
628 | }
629 |
630 | public void addSaturationBar(SaturationBar bar) {
631 | mSaturationBar = bar;
632 |
633 | mSaturationBar.setColorPicker(this);
634 | mSaturationBar.setColor(mColor);
635 | }
636 |
637 | public void addValueBar(ValueBar bar) {
638 | mValueBar = bar;
639 | mValueBar.setColorPicker(this);
640 | mValueBar.setColor(mColor);
641 | }
642 |
643 | /**
644 | * Change the color of the center which indicates the new color.
645 | *
646 | * @param color
647 | * int of the color.
648 | */
649 | public void setNewCenterColor(int color) {
650 | mCenterNewColor = color;
651 | mCenterNewPaint.setColor(color);
652 | if (mCenterOldColor == 0) {
653 | mCenterOldColor = color;
654 | mCenterOldPaint.setColor(color);
655 | }
656 | if (onColorChangedListener != null) {
657 | onColorChangedListener.onColorChanged(color);
658 | }
659 | invalidate();
660 | }
661 |
662 | /**
663 | * Change the color of the center which indicates the old color.
664 | *
665 | * @param color
666 | * int of the color.
667 | */
668 | public void setOldCenterColor(int color) {
669 | mCenterOldColor = color;
670 | mCenterOldPaint.setColor(color);
671 | invalidate();
672 | }
673 |
674 | public int getOldCenterColor() {
675 | return mCenterOldColor;
676 | }
677 |
678 | /**
679 | * Used to change the color of the {@code OpacityBar} used by the
680 | * {@code SVBar} if there is an change in color.
681 | *
682 | * @param color
683 | * int of the color used to change the opacity bar color.
684 | */
685 | public void changeOpacityBarColor(int color) {
686 | if (mOpacityBar != null) {
687 | mOpacityBar.setColor(color);
688 | }
689 | }
690 |
691 | /**
692 | * Used to change the color of the {@code SaturationBar}.
693 | *
694 | * @param color
695 | * int of the color used to change the opacity bar color.
696 | */
697 | public void changeSaturationBarColor(int color) {
698 | if (mSaturationBar != null) {
699 | mSaturationBar.setColor(color);
700 | }
701 | }
702 |
703 | /**
704 | * Used to change the color of the {@code ValueBar}.
705 | *
706 | * @param color
707 | * int of the color used to change the opacity bar color.
708 | */
709 | public void changeValueBarColor(int color) {
710 | if (mValueBar != null) {
711 | mValueBar.setColor(color);
712 | }
713 | }
714 |
715 | @Override
716 | protected Parcelable onSaveInstanceState() {
717 | Parcelable superState = super.onSaveInstanceState();
718 |
719 | Bundle state = new Bundle();
720 | state.putParcelable(STATE_PARENT, superState);
721 | state.putFloat(STATE_ANGLE, mAngle);
722 | state.putInt(STATE_OLD_COLOR, mCenterOldColor);
723 |
724 | return state;
725 | }
726 |
727 | @Override
728 | protected void onRestoreInstanceState(Parcelable state) {
729 | Bundle savedState = (Bundle) state;
730 |
731 | Parcelable superState = savedState.getParcelable(STATE_PARENT);
732 | super.onRestoreInstanceState(superState);
733 |
734 | mAngle = savedState.getFloat(STATE_ANGLE);
735 | setOldCenterColor(savedState.getInt(STATE_OLD_COLOR));
736 | mPointerColor.setColor(calculateColor(mAngle));
737 | }
738 | }
739 |
--------------------------------------------------------------------------------
/src/com/larswerkman/colorpicker/OpacityBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Lars Werkman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.larswerkman.colorpicker;
18 |
19 | import android.content.Context;
20 | import android.content.res.Resources;
21 | import android.content.res.TypedArray;
22 | import android.graphics.*;
23 | import android.os.Bundle;
24 | import android.os.Parcelable;
25 | import android.util.AttributeSet;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import eu.inmite.android.gridwichterle.R;
29 |
30 | public class OpacityBar extends View {
31 |
32 | /*
33 | * Constants used to save/restore the instance state.
34 | */
35 | private static final String STATE_PARENT = "parent";
36 | private static final String STATE_COLOR = "color";
37 | private static final String STATE_OPACITY = "opacity";
38 |
39 | /**
40 | * The thickness of the bar.
41 | */
42 | private int mBarThickness;
43 |
44 | /**
45 | * The length of the bar.
46 | */
47 | private int mBarLength;
48 | private int mPreferredBarLength;
49 |
50 | /**
51 | * The radius of the pointer.
52 | */
53 | private int mBarPointerRadius;
54 |
55 | /**
56 | * The radius of the halo of the pointer.
57 | */
58 | private int mBarPointerHaloRadius;
59 |
60 | /**
61 | * The position of the pointer on the bar.
62 | */
63 | private int mBarPointerPosition;
64 |
65 | /**
66 | * {@code Paint} instance used to draw the bar.
67 | */
68 | private Paint mBarPaint;
69 |
70 | /**
71 | * {@code Paint} instance used to draw the pointer.
72 | */
73 | private Paint mBarPointerPaint;
74 |
75 | /**
76 | * {@code Paint} instance used to draw the halo of the pointer.
77 | */
78 | private Paint mBarPointerHaloPaint;
79 |
80 | /**
81 | * The rectangle enclosing the bar.
82 | */
83 | private RectF mBarRect = new RectF();
84 |
85 | /**
86 | * {@code Shader} instance used to fill the shader of the paint.
87 | */
88 | private Shader shader;
89 |
90 | /**
91 | * {@code true} if the user clicked on the pointer to start the move mode.
92 | * {@code false} once the user stops touching the screen.
93 | *
94 | * @see #onTouchEvent(MotionEvent)
95 | */
96 | private boolean mIsMovingPointer;
97 |
98 | /**
99 | * The ARGB value of the currently selected color.
100 | */
101 | private int mColor;
102 |
103 | /**
104 | * An array of floats that can be build into a {@code Color}
105 | * Where we can extract the color from.
106 | */
107 | private float[] mHSVColor = new float[3];
108 |
109 | /**
110 | * Factor used to calculate the position to the Opacity on the bar.
111 | */
112 | private float mPosToOpacFactor;
113 |
114 | /**
115 | * Factor used to calculate the Opacity to the postion on the bar.
116 | */
117 | private float mOpacToPosFactor;
118 |
119 | /**
120 | * {@code ColorPicker} instance used to control the ColorPicker.
121 | */
122 | private ColorPicker mPicker = null;
123 |
124 | public OpacityBar(Context context) {
125 | super(context);
126 | init(null, 0);
127 | }
128 |
129 | public OpacityBar(Context context, AttributeSet attrs) {
130 | super(context, attrs);
131 | init(attrs, 0);
132 | }
133 |
134 | public OpacityBar(Context context, AttributeSet attrs, int defStyle) {
135 | super(context, attrs, defStyle);
136 | init(attrs, defStyle);
137 | }
138 |
139 | private void init(AttributeSet attrs, int defStyle) {
140 | final TypedArray a = getContext().obtainStyledAttributes(attrs,
141 | R.styleable.ColorBars, defStyle, 0);
142 | final Resources b = getContext().getResources();
143 |
144 | mBarThickness = a.getDimensionPixelSize(
145 | R.styleable.ColorBars_bar_thickness,
146 | b.getDimensionPixelSize(R.dimen.bar_thickness));
147 | mBarLength = a.getDimensionPixelSize(R.styleable.ColorBars_bar_length,
148 | b.getDimensionPixelSize(R.dimen.bar_length));
149 | mPreferredBarLength = mBarLength;
150 | mBarPointerRadius = a.getDimensionPixelSize(
151 | R.styleable.ColorBars_bar_pointer_radius,
152 | b.getDimensionPixelSize(R.dimen.bar_pointer_radius));
153 | mBarPointerHaloRadius = a.getDimensionPixelSize(
154 | R.styleable.ColorBars_bar_pointer_halo_radius,
155 | b.getDimensionPixelSize(R.dimen.bar_pointer_halo_radius));
156 |
157 | a.recycle();
158 |
159 | mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
160 | mBarPaint.setShader(shader);
161 |
162 | mBarPointerPosition = mBarLength + mBarPointerHaloRadius;
163 |
164 | mBarPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
165 | mBarPointerHaloPaint.setColor(Color.BLACK);
166 | mBarPointerHaloPaint.setAlpha(0x50);
167 |
168 | mBarPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
169 | mBarPointerPaint.setColor(0xff81ff00);
170 |
171 | mPosToOpacFactor = 0xFF / ((float) mBarLength);
172 | mOpacToPosFactor = ((float) mBarLength) / 0xFF;
173 | }
174 |
175 | @Override
176 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
177 | final int intrinsicSize = mPreferredBarLength
178 | + (mBarPointerHaloRadius * 2);
179 |
180 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
181 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
182 |
183 | int width;
184 | if (widthMode == MeasureSpec.EXACTLY) {
185 | width = widthSize;
186 | } else if (widthMode == MeasureSpec.AT_MOST) {
187 | width = Math.min(intrinsicSize, widthSize);
188 | } else {
189 | width = intrinsicSize;
190 | }
191 |
192 | mBarLength = width - (mBarPointerHaloRadius * 2);
193 | setMeasuredDimension((mBarLength + (mBarPointerHaloRadius * 2)),
194 | (mBarPointerHaloRadius * 2));
195 | }
196 |
197 | @Override
198 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
199 | super.onSizeChanged(w, h, oldw, oldh);
200 | mBarLength = w - (mBarPointerHaloRadius * 2);
201 |
202 | // Fill the rectangle instance.
203 | mBarRect.set(mBarPointerHaloRadius,
204 | (mBarPointerHaloRadius - (mBarThickness / 2)),
205 | (mBarLength + (mBarPointerHaloRadius)),
206 | (mBarPointerHaloRadius + (mBarThickness / 2)));
207 |
208 | // Update variables that depend of mBarLength.
209 | if(!isInEditMode()){
210 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
211 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
212 | Color.HSVToColor(0x00, mHSVColor),
213 | Color.HSVToColor(0xFF, mHSVColor) }, null,
214 | Shader.TileMode.CLAMP);
215 | } else {
216 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
217 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
218 | 0x0081ff00, 0xff81ff00 }, null, Shader.TileMode.CLAMP);
219 | Color.colorToHSV(0xff81ff00, mHSVColor);
220 | }
221 |
222 | mBarPaint.setShader(shader);
223 | mPosToOpacFactor = 0xFF / ((float) mBarLength);
224 | mOpacToPosFactor = ((float) mBarLength) / 0xFF;
225 | if(!isInEditMode()){
226 | mBarPointerPosition = Math.round((mOpacToPosFactor * Color
227 | .alpha(mColor))) + mBarPointerHaloRadius;
228 | } else {
229 | mBarPointerPosition = mBarLength + mBarPointerHaloRadius;
230 | }
231 | }
232 |
233 | @Override
234 | protected void onDraw(Canvas canvas) {
235 | // Draw the bar.
236 | canvas.drawRect(mBarRect, mBarPaint);
237 | // Draw the pointer halo.
238 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
239 | mBarPointerHaloRadius, mBarPointerHaloPaint);
240 | // Draw the pointer.
241 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
242 | mBarPointerRadius, mBarPointerPaint);
243 | };
244 |
245 | @Override
246 | public boolean onTouchEvent(MotionEvent event) {
247 | getParent().requestDisallowInterceptTouchEvent(true);
248 |
249 | // Convert coordinates to our internal coordinate system
250 | float x = event.getX();
251 |
252 | switch (event.getAction()) {
253 | case MotionEvent.ACTION_DOWN:
254 | mIsMovingPointer = true;
255 | // Check whether the user pressed on (or near) the pointer
256 | if (x >= (mBarPointerHaloRadius)
257 | && x <= (mBarPointerHaloRadius + mBarLength)) {
258 | mBarPointerPosition = Math.round(x);
259 | calculateColor(Math.round(x));
260 | mBarPointerPaint.setColor(mColor);
261 | invalidate();
262 | }
263 | break;
264 | case MotionEvent.ACTION_MOVE:
265 | if (mIsMovingPointer) {
266 | // Move the the pointer on the bar.
267 | if (x >= mBarPointerHaloRadius
268 | && x <= (mBarPointerHaloRadius + mBarLength)) {
269 | mBarPointerPosition = Math.round(x);
270 | calculateColor(Math.round(x));
271 | mBarPointerPaint.setColor(mColor);
272 | if (mPicker != null) {
273 | mPicker.setNewCenterColor(mColor);
274 | }
275 | invalidate();
276 | } else if (x < mBarPointerHaloRadius) {
277 | mBarPointerPosition = mBarPointerHaloRadius;
278 | mColor = Color.TRANSPARENT;
279 | mBarPointerPaint.setColor(mColor);
280 | if (mPicker != null) {
281 | mPicker.setNewCenterColor(mColor);
282 | }
283 | invalidate();
284 | } else if (x > (mBarPointerHaloRadius + mBarLength)) {
285 | mBarPointerPosition = mBarPointerHaloRadius + mBarLength;
286 | mColor = Color.HSVToColor(mHSVColor);
287 | mBarPointerPaint.setColor(mColor);
288 | if (mPicker != null) {
289 | mPicker.setNewCenterColor(mColor);
290 | }
291 | invalidate();
292 | }
293 | }
294 | break;
295 | case MotionEvent.ACTION_UP:
296 | mIsMovingPointer = false;
297 | break;
298 | }
299 | return true;
300 | }
301 |
302 | /**
303 | * Set the bar color.
304 | *
305 | * Its discouraged to use this method.
306 | *
307 | * @param color
308 | */
309 | public void setColor(int color) {
310 | Color.colorToHSV(color, mHSVColor);
311 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
312 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
313 | Color.HSVToColor(0x00, mHSVColor), color }, null,
314 | Shader.TileMode.CLAMP);
315 | mBarPaint.setShader(shader);
316 | calculateColor(mBarPointerPosition);
317 | mBarPointerPaint.setColor(mColor);
318 | if (mPicker != null) {
319 | mPicker.setNewCenterColor(mColor);
320 | }
321 | invalidate();
322 | }
323 |
324 | /**
325 | * Set the pointer on the bar. With the opacity value.
326 | *
327 | * @param saturation
328 | * float between 0 > 255
329 | */
330 | public void setOpacity(int opacity) {
331 | mBarPointerPosition = Math.round((mOpacToPosFactor * opacity))
332 | + mBarPointerHaloRadius;
333 | calculateColor(mBarPointerPosition);
334 | mBarPointerPaint.setColor(mColor);
335 | if (mPicker != null) {
336 | mPicker.setNewCenterColor(mColor);
337 | }
338 | invalidate();
339 | }
340 |
341 | /**
342 | * Get the currently selected opacity.
343 | *
344 | * @return The int value of the currently selected opacity.
345 | */
346 | public int getOpacity() {
347 | int opacity = Math
348 | .round((mPosToOpacFactor * (mBarPointerPosition - mBarPointerHaloRadius)));
349 | if (opacity < 5) {
350 | return 0x00;
351 | } else if (opacity > 250) {
352 | return 0xFF;
353 | } else {
354 | return opacity;
355 | }
356 | }
357 |
358 | /**
359 | * Calculate the color selected by the pointer on the bar.
360 | *
361 | * @param x
362 | * X-Coordinate of the pointer.
363 | */
364 | private void calculateColor(int x) {
365 | x = x - mBarPointerHaloRadius;
366 | if (x < 0) {
367 | x = 0;
368 | } else if (x > mBarLength) {
369 | x = mBarLength;
370 | }
371 |
372 | mColor = Color.HSVToColor(
373 | Math.round(mPosToOpacFactor * x),
374 | mHSVColor);
375 | if (Color.alpha(mColor) > 250) {
376 | mColor = Color.HSVToColor(mHSVColor);
377 | } else if (Color.alpha(mColor) < 5) {
378 | mColor = Color.TRANSPARENT;
379 | }
380 | }
381 |
382 | /**
383 | * Get the currently selected color.
384 | *
385 | * @return The ARGB value of the currently selected color.
386 | */
387 | public int getColor() {
388 | return mColor;
389 | }
390 |
391 | /**
392 | * Adds a {@code ColorPicker} instance to the bar.
393 | *
394 | * WARNING: Don't change the color picker. it is done already when the bar
395 | * is added to the ColorPicker
396 | *
397 | * @see ColorPicker#addSVBar(SVBar)
398 | * @param picker
399 | */
400 | public void setColorPicker(ColorPicker picker) {
401 | mPicker = picker;
402 | }
403 |
404 | @Override
405 | protected Parcelable onSaveInstanceState() {
406 | Parcelable superState = super.onSaveInstanceState();
407 |
408 | Bundle state = new Bundle();
409 | state.putParcelable(STATE_PARENT, superState);
410 | state.putFloatArray(STATE_COLOR, mHSVColor);
411 | state.putInt(STATE_OPACITY, getOpacity());
412 |
413 | return state;
414 | }
415 |
416 | @Override
417 | protected void onRestoreInstanceState(Parcelable state) {
418 | Bundle savedState = (Bundle) state;
419 |
420 | Parcelable superState = savedState.getParcelable(STATE_PARENT);
421 | super.onRestoreInstanceState(superState);
422 |
423 | setColor(Color.HSVToColor(savedState.getFloatArray(STATE_COLOR)));
424 | setOpacity(savedState.getInt(STATE_OPACITY));
425 | }
426 | }
--------------------------------------------------------------------------------
/src/com/larswerkman/colorpicker/SVBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Lars Werkman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.larswerkman.colorpicker;
18 |
19 | import android.content.Context;
20 | import android.content.res.Resources;
21 | import android.content.res.TypedArray;
22 | import android.graphics.*;
23 | import android.os.Bundle;
24 | import android.os.Parcelable;
25 | import android.util.AttributeSet;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import eu.inmite.android.gridwichterle.R;
29 |
30 | public class SVBar extends View {
31 |
32 | /*
33 | * Constants used to save/restore the instance state.
34 | */
35 | private static final String STATE_PARENT = "parent";
36 | private static final String STATE_COLOR = "color";
37 | private static final String STATE_SATURATION = "saturation";
38 | private static final String STATE_VALUE = "value";
39 |
40 | /**
41 | * The thickness of the bar.
42 | */
43 | private int mBarThickness;
44 |
45 | /**
46 | * The length of the bar.
47 | */
48 | private int mBarLength;
49 | private int mPreferredBarLength;
50 |
51 | /**
52 | * The radius of the pointer.
53 | */
54 | private int mBarPointerRadius;
55 |
56 | /**
57 | * The radius of the halo of the pointer.
58 | */
59 | private int mBarPointerHaloRadius;
60 |
61 | /**
62 | * The position of the pointer on the bar.
63 | */
64 | private int mBarPointerPosition;
65 |
66 | /**
67 | * {@code Paint} instance used to draw the bar.
68 | */
69 | private Paint mBarPaint;
70 |
71 | /**
72 | * {@code Paint} instance used to draw the pointer.
73 | */
74 | private Paint mBarPointerPaint;
75 |
76 | /**
77 | * {@code Paint} instance used to draw the halo of the pointer.
78 | */
79 | private Paint mBarPointerHaloPaint;
80 |
81 | /**
82 | * The rectangle enclosing the bar.
83 | */
84 | private RectF mBarRect = new RectF();
85 |
86 | /**
87 | * {@code Shader} instance used to fill the shader of the paint.
88 | */
89 | private Shader shader;
90 |
91 | /**
92 | * {@code true} if the user clicked on the pointer to start the move mode.
93 | * {@code false} once the user stops touching the screen.
94 | *
95 | * @see #onTouchEvent(MotionEvent)
96 | */
97 | private boolean mIsMovingPointer;
98 |
99 | /**
100 | * The ARGB value of the currently selected color.
101 | */
102 | private int mColor;
103 |
104 | /**
105 | * An array of floats that can be build into a {@code Color}
106 | * Where we can extract the Saturation and Value from.
107 | */
108 | private float[] mHSVColor = new float[3];
109 |
110 | /**
111 | * Factor used to calculate the position to the Saturation/Value on the bar.
112 | */
113 | private float mPosToSVFactor;
114 |
115 | /**
116 | * Factor used to calculate the Saturation/Value to the postion on the bar.
117 | */
118 | private float mSVToPosFactor;
119 |
120 | /**
121 | * {@code ColorPicker} instance used to control the ColorPicker.
122 | */
123 | private ColorPicker mPicker = null;
124 |
125 | public SVBar(Context context) {
126 | super(context);
127 | init(null, 0);
128 | }
129 |
130 | public SVBar(Context context, AttributeSet attrs) {
131 | super(context, attrs);
132 | init(attrs, 0);
133 | }
134 |
135 | public SVBar(Context context, AttributeSet attrs, int defStyle) {
136 | super(context, attrs, defStyle);
137 | init(attrs, defStyle);
138 | }
139 |
140 | private void init(AttributeSet attrs, int defStyle) {
141 | final TypedArray a = getContext().obtainStyledAttributes(attrs,
142 | R.styleable.ColorBars, defStyle, 0);
143 | final Resources b = getContext().getResources();
144 |
145 | mBarThickness = a.getDimensionPixelSize(
146 | R.styleable.ColorBars_bar_thickness,
147 | b.getDimensionPixelSize(R.dimen.bar_thickness));
148 | mBarLength = a.getDimensionPixelSize(R.styleable.ColorBars_bar_length,
149 | b.getDimensionPixelSize(R.dimen.bar_length));
150 | mPreferredBarLength = mBarLength;
151 | mBarPointerRadius = a.getDimensionPixelSize(
152 | R.styleable.ColorBars_bar_pointer_radius,
153 | b.getDimensionPixelSize(R.dimen.bar_pointer_radius));
154 | mBarPointerHaloRadius = a.getDimensionPixelSize(
155 | R.styleable.ColorBars_bar_pointer_halo_radius,
156 | b.getDimensionPixelSize(R.dimen.bar_pointer_halo_radius));
157 |
158 | a.recycle();
159 |
160 | mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
161 | mBarPaint.setShader(shader);
162 |
163 | mBarPointerPosition = (mBarLength / 2) + mBarPointerHaloRadius;
164 |
165 | mBarPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
166 | mBarPointerHaloPaint.setColor(Color.BLACK);
167 | mBarPointerHaloPaint.setAlpha(0x50);
168 |
169 | mBarPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
170 | mBarPointerPaint.setColor(0xff81ff00);
171 |
172 | mPosToSVFactor = 1 / ((float) mBarLength / 2);
173 | mSVToPosFactor = ((float) mBarLength / 2) / 1;
174 |
175 | }
176 |
177 | @Override
178 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
179 | final int intrinsicSize = mPreferredBarLength
180 | + (mBarPointerHaloRadius * 2);
181 |
182 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
183 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
184 |
185 | int width;
186 | if (widthMode == MeasureSpec.EXACTLY) {
187 | width = widthSize;
188 | } else if (widthMode == MeasureSpec.AT_MOST) {
189 | width = Math.min(intrinsicSize, widthSize);
190 | } else {
191 | width = intrinsicSize;
192 | }
193 |
194 | mBarLength = width - (mBarPointerHaloRadius * 2);
195 | setMeasuredDimension((mBarLength + (mBarPointerHaloRadius * 2)),
196 | (mBarPointerHaloRadius * 2));
197 | }
198 |
199 | @Override
200 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
201 | super.onSizeChanged(w, h, oldw, oldh);
202 | mBarLength = w - (mBarPointerHaloRadius * 2);
203 |
204 | // Fill the rectangle instance.
205 | mBarRect.set(mBarPointerHaloRadius,
206 | (mBarPointerHaloRadius - (mBarThickness / 2)),
207 | (mBarLength + (mBarPointerHaloRadius)),
208 | (mBarPointerHaloRadius + (mBarThickness / 2)));
209 |
210 | // Update variables that depend of mBarLength.
211 | if(!isInEditMode()){
212 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
213 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
214 | 0xffffffff, Color.HSVToColor(mHSVColor), 0xff000000 },
215 | null, Shader.TileMode.CLAMP);
216 | } else {
217 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
218 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
219 | 0xffffffff, 0xff81ff00, 0xff000000 }, null,
220 | Shader.TileMode.CLAMP);
221 | Color.colorToHSV(0xff81ff00, mHSVColor);
222 | }
223 |
224 | mBarPaint.setShader(shader);
225 | mPosToSVFactor = 1 / ((float) mBarLength / 2);
226 | mSVToPosFactor = ((float) mBarLength / 2) / 1;
227 | float[] hsvColor = new float[3];
228 | Color.colorToHSV(mColor, hsvColor);
229 | if (hsvColor[1] < hsvColor[2]) {
230 | mBarPointerPosition = Math.round((mSVToPosFactor * hsvColor[1])
231 | + mBarPointerHaloRadius);
232 | } else {
233 | mBarPointerPosition = Math
234 | .round((mSVToPosFactor * (1 - hsvColor[2]))
235 | + mBarPointerHaloRadius + (mBarLength / 2));
236 | }
237 | if(isInEditMode()){
238 | mBarPointerPosition = (mBarLength / 2) + mBarPointerHaloRadius;
239 | }
240 | }
241 |
242 | @Override
243 | protected void onDraw(Canvas canvas) {
244 | // Draw the bar.
245 | canvas.drawRect(mBarRect, mBarPaint);
246 | // Draw the pointer halo.
247 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
248 | mBarPointerHaloRadius, mBarPointerHaloPaint);
249 | // Draw the pointer.
250 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
251 | mBarPointerRadius, mBarPointerPaint);
252 | };
253 |
254 | @Override
255 | public boolean onTouchEvent(MotionEvent event) {
256 | getParent().requestDisallowInterceptTouchEvent(true);
257 |
258 | // Convert coordinates to our internal coordinate system
259 | float x = event.getX();
260 |
261 | switch (event.getAction()) {
262 | case MotionEvent.ACTION_DOWN:
263 | mIsMovingPointer = true;
264 | // Check whether the user pressed on the pointer
265 | if (x >= (mBarPointerHaloRadius)
266 | && x <= (mBarPointerHaloRadius + mBarLength)) {
267 | mBarPointerPosition = Math.round(x);
268 | calculateColor(Math.round(x));
269 | mBarPointerPaint.setColor(mColor);
270 | invalidate();
271 | }
272 | break;
273 | case MotionEvent.ACTION_MOVE:
274 | if (mIsMovingPointer) {
275 | // Move the the pointer on the bar.
276 | if (x >= mBarPointerHaloRadius
277 | && x <= (mBarPointerHaloRadius + mBarLength)) {
278 | mBarPointerPosition = Math.round(x);
279 | calculateColor(Math.round(x));
280 | mBarPointerPaint.setColor(mColor);
281 | if (mPicker != null) {
282 | mPicker.setNewCenterColor(mColor);
283 | mPicker.changeOpacityBarColor(mColor);
284 | }
285 | invalidate();
286 | } else if (x < mBarPointerHaloRadius) {
287 | mBarPointerPosition = mBarPointerHaloRadius;
288 | mColor = Color.WHITE;
289 | mBarPointerPaint.setColor(mColor);
290 | if (mPicker != null) {
291 | mPicker.setNewCenterColor(mColor);
292 | mPicker.changeOpacityBarColor(mColor);
293 | }
294 | invalidate();
295 | } else if (x > (mBarPointerHaloRadius + mBarLength)) {
296 | mBarPointerPosition = mBarPointerHaloRadius + mBarLength;
297 | mColor = Color.BLACK;
298 | mBarPointerPaint.setColor(mColor);
299 | if (mPicker != null) {
300 | mPicker.setNewCenterColor(mColor);
301 | mPicker.changeOpacityBarColor(mColor);
302 | }
303 | invalidate();
304 | }
305 | }
306 | break;
307 | case MotionEvent.ACTION_UP:
308 | mIsMovingPointer = false;
309 | break;
310 | }
311 | return true;
312 | }
313 |
314 | /**
315 | * Set the pointer on the bar. With the saturation value.
316 | *
317 | * @param saturation
318 | * float between 0 > 1
319 | */
320 | public void setSaturation(float saturation) {
321 | mBarPointerPosition = Math.round((mSVToPosFactor * saturation)
322 | + mBarPointerHaloRadius);
323 | calculateColor(mBarPointerPosition);
324 | mBarPointerPaint.setColor(mColor);
325 | // Check whether the Saturation/Value bar is added to the ColorPicker
326 | // wheel
327 | if (mPicker != null) {
328 | mPicker.setNewCenterColor(mColor);
329 | mPicker.changeOpacityBarColor(mColor);
330 | }
331 | invalidate();
332 | }
333 |
334 | /**
335 | * Set the pointer on the bar. With the Value value.
336 | *
337 | * @param value
338 | * float between 0 > 1
339 | */
340 | public void setValue(float value) {
341 | mBarPointerPosition = Math.round((mSVToPosFactor * (1 - value))
342 | + mBarPointerHaloRadius + (mBarLength / 2));
343 | calculateColor(mBarPointerPosition);
344 | mBarPointerPaint.setColor(mColor);
345 | // Check whether the Saturation/Value bar is added to the ColorPicker
346 | // wheel
347 | if (mPicker != null) {
348 | mPicker.setNewCenterColor(mColor);
349 | mPicker.changeOpacityBarColor(mColor);
350 | }
351 | invalidate();
352 | }
353 |
354 | /**
355 | * Set the bar color.
356 | *
357 | * Its discouraged to use this method.
358 | *
359 | * @param color
360 | */
361 | public void setColor(int color) {
362 | Color.colorToHSV(color, mHSVColor);
363 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
364 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
365 | 0xffffffff, color, 0xff000000 }, null,
366 | Shader.TileMode.CLAMP);
367 | mBarPaint.setShader(shader);
368 | calculateColor(mBarPointerPosition);
369 | mBarPointerPaint.setColor(mColor);
370 | if (mPicker != null) {
371 | mPicker.setNewCenterColor(mColor);
372 | mPicker.changeOpacityBarColor(mColor);
373 | }
374 | invalidate();
375 | }
376 |
377 | /**
378 | * Calculate the color selected by the pointer on the bar.
379 | *
380 | * @param x
381 | * X-Coordinate of the pointer.
382 | */
383 | private void calculateColor(int x) {
384 | if (x > (mBarPointerHaloRadius + (mBarLength / 2))
385 | && x < (mBarPointerHaloRadius + mBarLength)) {
386 | mColor = Color
387 | .HSVToColor(new float[] {
388 | mHSVColor[0],
389 | 1f,
390 | (float) (1 - (mPosToSVFactor * (x - (mBarPointerHaloRadius + (mBarLength / 2))))) });
391 | } else if (x > mBarPointerHaloRadius
392 | && x < (mBarPointerHaloRadius + mBarLength)) {
393 | mColor = Color.HSVToColor(new float[] { mHSVColor[0],
394 | (float) ((mPosToSVFactor * (x - mBarPointerHaloRadius))),
395 | 1f });
396 | } else if (x == mBarPointerHaloRadius) {
397 | mColor = Color.WHITE;
398 | } else if (x == mBarPointerHaloRadius + mBarLength) {
399 | mColor = Color.BLACK;
400 | }
401 | }
402 |
403 | /**
404 | * Get the currently selected color.
405 | *
406 | * @return The ARGB value of the currently selected color.
407 | */
408 | public int getColor() {
409 | return mColor;
410 | }
411 |
412 | /**
413 | * Adds a {@code ColorPicker} instance to the bar.
414 | *
415 | * WARNING: Don't change the color picker. it is done already when the bar
416 | * is added to the ColorPicker
417 | *
418 | * @see ColorPicker#addSVBar(SVBar)
419 | * @param picker
420 | */
421 | public void setColorPicker(ColorPicker picker) {
422 | mPicker = picker;
423 | }
424 |
425 | @Override
426 | protected Parcelable onSaveInstanceState() {
427 | Parcelable superState = super.onSaveInstanceState();
428 |
429 | Bundle state = new Bundle();
430 | state.putParcelable(STATE_PARENT, superState);
431 | state.putFloatArray(STATE_COLOR, mHSVColor);
432 | float[] hsvColor = new float[3];
433 | Color.colorToHSV(mColor, hsvColor);
434 | if (hsvColor[1] < hsvColor[2]) {
435 | state.putFloat(STATE_SATURATION, hsvColor[1]);
436 | } else {
437 | state.putFloat(STATE_VALUE, hsvColor[2]);
438 | }
439 |
440 | return state;
441 | }
442 |
443 | @Override
444 | protected void onRestoreInstanceState(Parcelable state) {
445 | Bundle savedState = (Bundle) state;
446 |
447 | Parcelable superState = savedState.getParcelable(STATE_PARENT);
448 | super.onRestoreInstanceState(superState);
449 |
450 | setColor(Color.HSVToColor(savedState.getFloatArray(STATE_COLOR)));
451 | if (savedState.containsKey(STATE_SATURATION)) {
452 | setSaturation(savedState.getFloat(STATE_SATURATION));
453 | } else {
454 | setValue(savedState.getFloat(STATE_VALUE));
455 | }
456 | }
457 | }
--------------------------------------------------------------------------------
/src/com/larswerkman/colorpicker/SaturationBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Lars Werkman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.larswerkman.colorpicker;
18 |
19 | import android.content.Context;
20 | import android.content.res.Resources;
21 | import android.content.res.TypedArray;
22 | import android.graphics.*;
23 | import android.os.Bundle;
24 | import android.os.Parcelable;
25 | import android.util.AttributeSet;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import eu.inmite.android.gridwichterle.R;
29 |
30 | public class SaturationBar extends View {
31 |
32 | /*
33 | * Constants used to save/restore the instance state.
34 | */
35 | private static final String STATE_PARENT = "parent";
36 | private static final String STATE_COLOR = "color";
37 | private static final String STATE_SATURATION = "saturation";
38 |
39 | /**
40 | * The thickness of the bar.
41 | */
42 | private int mBarThickness;
43 |
44 | /**
45 | * The length of the bar.
46 | */
47 | private int mBarLength;
48 | private int mPreferredBarLength;
49 |
50 | /**
51 | * The radius of the pointer.
52 | */
53 | private int mBarPointerRadius;
54 |
55 | /**
56 | * The radius of the halo of the pointer.
57 | */
58 | private int mBarPointerHaloRadius;
59 |
60 | /**
61 | * The position of the pointer on the bar.
62 | */
63 | private int mBarPointerPosition;
64 |
65 | /**
66 | * {@code Paint} instance used to draw the bar.
67 | */
68 | private Paint mBarPaint;
69 |
70 | /**
71 | * {@code Paint} instance used to draw the pointer.
72 | */
73 | private Paint mBarPointerPaint;
74 |
75 | /**
76 | * {@code Paint} instance used to draw the halo of the pointer.
77 | */
78 | private Paint mBarPointerHaloPaint;
79 |
80 | /**
81 | * The rectangle enclosing the bar.
82 | */
83 | private RectF mBarRect = new RectF();
84 |
85 | /**
86 | * {@code Shader} instance used to fill the shader of the paint.
87 | */
88 | private Shader shader;
89 |
90 | /**
91 | * {@code true} if the user clicked on the pointer to start the move mode.
92 | * {@code false} once the user stops touching the screen.
93 | *
94 | * @see #onTouchEvent(MotionEvent)
95 | */
96 | private boolean mIsMovingPointer;
97 |
98 | /**
99 | * The ARGB value of the currently selected color.
100 | */
101 | private int mColor;
102 |
103 | /**
104 | * An array of floats that can be build into a {@code Color}
105 | * Where we can extract the color from.
106 | */
107 | private float[] mHSVColor = new float[3];
108 |
109 | /**
110 | * Factor used to calculate the position to the Opacity on the bar.
111 | */
112 | private float mPosToSatFactor;
113 |
114 | /**
115 | * Factor used to calculate the Opacity to the postion on the bar.
116 | */
117 | private float mSatToPosFactor;
118 |
119 | /**
120 | * {@code ColorPicker} instance used to control the ColorPicker.
121 | */
122 | private ColorPicker mPicker = null;
123 |
124 | public SaturationBar(Context context) {
125 | super(context);
126 | init(null, 0);
127 | }
128 |
129 | public SaturationBar(Context context, AttributeSet attrs) {
130 | super(context, attrs);
131 | init(attrs, 0);
132 | }
133 |
134 | public SaturationBar(Context context, AttributeSet attrs, int defStyle) {
135 | super(context, attrs, defStyle);
136 | init(attrs, defStyle);
137 | }
138 |
139 | private void init(AttributeSet attrs, int defStyle) {
140 | final TypedArray a = getContext().obtainStyledAttributes(attrs,
141 | R.styleable.ColorBars, defStyle, 0);
142 | final Resources b = getContext().getResources();
143 |
144 | mBarThickness = a.getDimensionPixelSize(
145 | R.styleable.ColorBars_bar_thickness,
146 | b.getDimensionPixelSize(R.dimen.bar_thickness));
147 | mBarLength = a.getDimensionPixelSize(R.styleable.ColorBars_bar_length,
148 | b.getDimensionPixelSize(R.dimen.bar_length));
149 | mPreferredBarLength = mBarLength;
150 | mBarPointerRadius = a.getDimensionPixelSize(
151 | R.styleable.ColorBars_bar_pointer_radius,
152 | b.getDimensionPixelSize(R.dimen.bar_pointer_radius));
153 | mBarPointerHaloRadius = a.getDimensionPixelSize(
154 | R.styleable.ColorBars_bar_pointer_halo_radius,
155 | b.getDimensionPixelSize(R.dimen.bar_pointer_halo_radius));
156 |
157 | a.recycle();
158 |
159 | mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
160 | mBarPaint.setShader(shader);
161 |
162 | mBarPointerPosition = mBarLength + mBarPointerHaloRadius;
163 |
164 | mBarPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
165 | mBarPointerHaloPaint.setColor(Color.BLACK);
166 | mBarPointerHaloPaint.setAlpha(0x50);
167 |
168 | mBarPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
169 | mBarPointerPaint.setColor(0xff81ff00);
170 |
171 | mPosToSatFactor = 1 / ((float) mBarLength);
172 | mSatToPosFactor = ((float) mBarLength) / 1;
173 | }
174 |
175 | @Override
176 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
177 | final int intrinsicSize = mPreferredBarLength
178 | + (mBarPointerHaloRadius * 2);
179 |
180 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
181 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
182 |
183 | int width;
184 | if (widthMode == MeasureSpec.EXACTLY) {
185 | width = widthSize;
186 | } else if (widthMode == MeasureSpec.AT_MOST) {
187 | width = Math.min(intrinsicSize, widthSize);
188 | } else {
189 | width = intrinsicSize;
190 | }
191 |
192 | mBarLength = width - (mBarPointerHaloRadius * 2);
193 | setMeasuredDimension((mBarLength + (mBarPointerHaloRadius * 2)),
194 | (mBarPointerHaloRadius * 2));
195 | }
196 |
197 | @Override
198 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
199 | super.onSizeChanged(w, h, oldw, oldh);
200 | mBarLength = w - (mBarPointerHaloRadius * 2);
201 |
202 | // Fill the rectangle instance.
203 | mBarRect.set(mBarPointerHaloRadius,
204 | (mBarPointerHaloRadius - (mBarThickness / 2)),
205 | (mBarLength + (mBarPointerHaloRadius)),
206 | (mBarPointerHaloRadius + (mBarThickness / 2)));
207 |
208 | // Update variables that depend of mBarLength.
209 | if(!isInEditMode()){
210 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
211 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
212 | Color.WHITE,
213 | Color.HSVToColor(0xFF, mHSVColor) }, null,
214 | Shader.TileMode.CLAMP);
215 | } else {
216 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
217 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
218 | Color.WHITE, 0xff81ff00 }, null, Shader.TileMode.CLAMP);
219 | Color.colorToHSV(0xff81ff00, mHSVColor);
220 | }
221 |
222 | mBarPaint.setShader(shader);
223 | mPosToSatFactor = 1 / ((float) mBarLength);
224 | mSatToPosFactor = ((float) mBarLength) / 1;
225 |
226 | float[] hsvColor = new float[3];
227 | Color.colorToHSV(mColor, hsvColor);
228 |
229 | if(!isInEditMode()){
230 | mBarPointerPosition = Math.round((mSatToPosFactor* hsvColor[1])
231 | + mBarPointerHaloRadius);
232 | } else {
233 | mBarPointerPosition = mBarLength + mBarPointerHaloRadius;
234 | }
235 | }
236 |
237 | @Override
238 | protected void onDraw(Canvas canvas) {
239 | // Draw the bar.
240 | canvas.drawRect(mBarRect, mBarPaint);
241 | // Draw the pointer halo.
242 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
243 | mBarPointerHaloRadius, mBarPointerHaloPaint);
244 | // Draw the pointer.
245 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
246 | mBarPointerRadius, mBarPointerPaint);
247 | };
248 |
249 | @Override
250 | public boolean onTouchEvent(MotionEvent event) {
251 | getParent().requestDisallowInterceptTouchEvent(true);
252 |
253 | // Convert coordinates to our internal coordinate system
254 | float x = event.getX();
255 |
256 | switch (event.getAction()) {
257 | case MotionEvent.ACTION_DOWN:
258 | mIsMovingPointer = true;
259 | // Check whether the user pressed on (or near) the pointer
260 | if (x >= (mBarPointerHaloRadius)
261 | && x <= (mBarPointerHaloRadius + mBarLength))
262 | {
263 | mBarPointerPosition = Math.round(x);
264 | calculateColor(Math.round(x));
265 | mBarPointerPaint.setColor(mColor);
266 | invalidate();
267 | }
268 | break;
269 | case MotionEvent.ACTION_MOVE:
270 | if (mIsMovingPointer) {
271 | // Move the the pointer on the bar.
272 | if (x >= mBarPointerHaloRadius
273 | && x <= (mBarPointerHaloRadius + mBarLength)) {
274 | mBarPointerPosition = Math.round(x);
275 | calculateColor(Math.round(x));
276 | mBarPointerPaint.setColor(mColor);
277 | if (mPicker != null) {
278 | mPicker.setNewCenterColor(mColor);
279 | mPicker.changeValueBarColor(mColor);
280 | mPicker.changeOpacityBarColor(mColor);
281 | }
282 | invalidate();
283 | } else if (x < mBarPointerHaloRadius) {
284 | mBarPointerPosition = mBarPointerHaloRadius;
285 | mColor = Color.WHITE;
286 | mBarPointerPaint.setColor(mColor);
287 | if (mPicker != null) {
288 | mPicker.setNewCenterColor(mColor);
289 | mPicker.changeValueBarColor(mColor);
290 | mPicker.changeOpacityBarColor(mColor);
291 | }
292 | invalidate();
293 | } else if (x > (mBarPointerHaloRadius + mBarLength)) {
294 | mBarPointerPosition = mBarPointerHaloRadius + mBarLength;
295 | mColor = Color.HSVToColor(mHSVColor);
296 | mBarPointerPaint.setColor(mColor);
297 | if (mPicker != null) {
298 | mPicker.setNewCenterColor(mColor);
299 | mPicker.changeValueBarColor(mColor);
300 | mPicker.changeOpacityBarColor(mColor);
301 | }
302 | invalidate();
303 | }
304 | }
305 | break;
306 | case MotionEvent.ACTION_UP:
307 | mIsMovingPointer = false;
308 | break;
309 | }
310 | return true;
311 | }
312 |
313 | /**
314 | * Set the bar color.
315 | *
316 | * Its discouraged to use this method.
317 | *
318 | * @param color
319 | */
320 | public void setColor(int color) {
321 | Color.colorToHSV(color, mHSVColor);
322 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
323 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
324 | Color.WHITE, color }, null,
325 | Shader.TileMode.CLAMP);
326 | mBarPaint.setShader(shader);
327 | calculateColor(mBarPointerPosition);
328 | mBarPointerPaint.setColor(mColor);
329 | if (mPicker != null) {
330 | mPicker.setNewCenterColor(mColor);
331 | mPicker.changeValueBarColor(mColor);
332 | mPicker.changeOpacityBarColor(mColor);
333 | }
334 | invalidate();
335 | }
336 |
337 | /**
338 | * Set the pointer on the bar. With the opacity value.
339 | *
340 | * @param saturation
341 | * float between 0 > 1
342 | */
343 | public void setSaturation(float saturation) {
344 | mBarPointerPosition = Math.round((mSatToPosFactor * saturation))
345 | + mBarPointerHaloRadius;
346 | calculateColor(mBarPointerPosition);
347 | mBarPointerPaint.setColor(mColor);
348 | if (mPicker != null) {
349 | mPicker.setNewCenterColor(mColor);
350 | mPicker.changeValueBarColor(mColor);
351 | mPicker.changeOpacityBarColor(mColor);
352 | }
353 | invalidate();
354 | }
355 |
356 | /**
357 | * Calculate the color selected by the pointer on the bar.
358 | *
359 | * @param x
360 | * X-Coordinate of the pointer.
361 | */
362 | private void calculateColor(int x) {
363 | x = x - mBarPointerHaloRadius;
364 | if (x < 0) {
365 | x = 0;
366 | } else if (x > mBarLength) {
367 | x = mBarLength;
368 | }
369 | mColor = Color.HSVToColor(new float[] { mHSVColor[0],
370 | (float) ((mPosToSatFactor * x)),
371 | 1f });
372 | }
373 |
374 | /**
375 | * Get the currently selected color.
376 | *
377 | * @return The ARGB value of the currently selected color.
378 | */
379 | public int getColor() {
380 | return mColor;
381 | }
382 |
383 | /**
384 | * Adds a {@code ColorPicker} instance to the bar.
385 | *
386 | * WARNING: Don't change the color picker. it is done already when the bar
387 | * is added to the ColorPicker
388 | *
389 | * @see ColorPicker#addSVBar(SVBar)
390 | * @param picker
391 | */
392 | public void setColorPicker(ColorPicker picker) {
393 | mPicker = picker;
394 | }
395 |
396 | @Override
397 | protected Parcelable onSaveInstanceState() {
398 | Parcelable superState = super.onSaveInstanceState();
399 |
400 | Bundle state = new Bundle();
401 | state.putParcelable(STATE_PARENT, superState);
402 | state.putFloatArray(STATE_COLOR, mHSVColor);
403 |
404 | float[] hsvColor = new float[3];
405 | Color.colorToHSV(mColor, hsvColor);
406 | state.putFloat(STATE_SATURATION, hsvColor[1]);
407 |
408 | return state;
409 | }
410 |
411 | @Override
412 | protected void onRestoreInstanceState(Parcelable state) {
413 | Bundle savedState = (Bundle) state;
414 |
415 | Parcelable superState = savedState.getParcelable(STATE_PARENT);
416 | super.onRestoreInstanceState(superState);
417 |
418 | setColor(Color.HSVToColor(savedState.getFloatArray(STATE_COLOR)));
419 | setSaturation(savedState.getFloat(STATE_SATURATION));
420 | }
421 | }
422 |
--------------------------------------------------------------------------------
/src/com/larswerkman/colorpicker/ValueBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Lars Werkman
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.larswerkman.colorpicker;
18 |
19 | import android.content.Context;
20 | import android.content.res.Resources;
21 | import android.content.res.TypedArray;
22 | import android.graphics.*;
23 | import android.os.Bundle;
24 | import android.os.Parcelable;
25 | import android.util.AttributeSet;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import eu.inmite.android.gridwichterle.R;
29 |
30 | public class ValueBar extends View {
31 |
32 | /*
33 | * Constants used to save/restore the instance state.
34 | */
35 | private static final String STATE_PARENT = "parent";
36 | private static final String STATE_COLOR = "color";
37 | private static final String STATE_VALUE = "value";
38 |
39 | /**
40 | * The thickness of the bar.
41 | */
42 | private int mBarThickness;
43 |
44 | /**
45 | * The length of the bar.
46 | */
47 | private int mBarLength;
48 | private int mPreferredBarLength;
49 |
50 | /**
51 | * The radius of the pointer.
52 | */
53 | private int mBarPointerRadius;
54 |
55 | /**
56 | * The radius of the halo of the pointer.
57 | */
58 | private int mBarPointerHaloRadius;
59 |
60 | /**
61 | * The position of the pointer on the bar.
62 | */
63 | private int mBarPointerPosition;
64 |
65 | /**
66 | * {@code Paint} instance used to draw the bar.
67 | */
68 | private Paint mBarPaint;
69 |
70 | /**
71 | * {@code Paint} instance used to draw the pointer.
72 | */
73 | private Paint mBarPointerPaint;
74 |
75 | /**
76 | * {@code Paint} instance used to draw the halo of the pointer.
77 | */
78 | private Paint mBarPointerHaloPaint;
79 |
80 | /**
81 | * The rectangle enclosing the bar.
82 | */
83 | private RectF mBarRect = new RectF();
84 |
85 | /**
86 | * {@code Shader} instance used to fill the shader of the paint.
87 | */
88 | private Shader shader;
89 |
90 | /**
91 | * {@code true} if the user clicked on the pointer to start the move mode.
92 | * {@code false} once the user stops touching the screen.
93 | *
94 | * @see #onTouchEvent(MotionEvent)
95 | */
96 | private boolean mIsMovingPointer;
97 |
98 | /**
99 | * The ARGB value of the currently selected color.
100 | */
101 | private int mColor;
102 |
103 | /**
104 | * An array of floats that can be build into a {@code Color}
105 | * Where we can extract the color from.
106 | */
107 | private float[] mHSVColor = new float[3];
108 |
109 | /**
110 | * Factor used to calculate the position to the Opacity on the bar.
111 | */
112 | private float mPosToSatFactor;
113 |
114 | /**
115 | * Factor used to calculate the Opacity to the postion on the bar.
116 | */
117 | private float mSatToPosFactor;
118 |
119 | /**
120 | * {@code ColorPicker} instance used to control the ColorPicker.
121 | */
122 | private ColorPicker mPicker = null;
123 |
124 | public ValueBar(Context context) {
125 | super(context);
126 | init(null, 0);
127 | }
128 |
129 | public ValueBar(Context context, AttributeSet attrs) {
130 | super(context, attrs);
131 | init(attrs, 0);
132 | }
133 |
134 | public ValueBar(Context context, AttributeSet attrs, int defStyle) {
135 | super(context, attrs, defStyle);
136 | init(attrs, defStyle);
137 | }
138 |
139 | private void init(AttributeSet attrs, int defStyle) {
140 | final TypedArray a = getContext().obtainStyledAttributes(attrs,
141 | R.styleable.ColorBars, defStyle, 0);
142 | final Resources b = getContext().getResources();
143 |
144 | mBarThickness = a.getDimensionPixelSize(
145 | R.styleable.ColorBars_bar_thickness,
146 | b.getDimensionPixelSize(R.dimen.bar_thickness));
147 | mBarLength = a.getDimensionPixelSize(R.styleable.ColorBars_bar_length,
148 | b.getDimensionPixelSize(R.dimen.bar_length));
149 | mPreferredBarLength = mBarLength;
150 | mBarPointerRadius = a.getDimensionPixelSize(
151 | R.styleable.ColorBars_bar_pointer_radius,
152 | b.getDimensionPixelSize(R.dimen.bar_pointer_radius));
153 | mBarPointerHaloRadius = a.getDimensionPixelSize(
154 | R.styleable.ColorBars_bar_pointer_halo_radius,
155 | b.getDimensionPixelSize(R.dimen.bar_pointer_halo_radius));
156 |
157 | a.recycle();
158 |
159 | mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
160 | mBarPaint.setShader(shader);
161 |
162 | mBarPointerPosition = mBarPointerHaloRadius;
163 |
164 | mBarPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
165 | mBarPointerHaloPaint.setColor(Color.BLACK);
166 | mBarPointerHaloPaint.setAlpha(0x50);
167 |
168 | mBarPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
169 | mBarPointerPaint.setColor(0xff81ff00);
170 |
171 | mPosToSatFactor = 1 / ((float) mBarLength);
172 | mSatToPosFactor = ((float) mBarLength) / 1;
173 | }
174 |
175 | @Override
176 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
177 | final int intrinsicSize = mPreferredBarLength
178 | + (mBarPointerHaloRadius * 2);
179 |
180 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
181 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
182 |
183 | int width;
184 | if (widthMode == MeasureSpec.EXACTLY) {
185 | width = widthSize;
186 | } else if (widthMode == MeasureSpec.AT_MOST) {
187 | width = Math.min(intrinsicSize, widthSize);
188 | } else {
189 | width = intrinsicSize;
190 | }
191 |
192 | mBarLength = width - (mBarPointerHaloRadius * 2);
193 | setMeasuredDimension((mBarLength + (mBarPointerHaloRadius * 2)),
194 | (mBarPointerHaloRadius * 2));
195 | }
196 |
197 | @Override
198 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
199 | super.onSizeChanged(w, h, oldw, oldh);
200 | mBarLength = w - (mBarPointerHaloRadius * 2);
201 |
202 | // Fill the rectangle instance.
203 | mBarRect.set(mBarPointerHaloRadius,
204 | (mBarPointerHaloRadius - (mBarThickness / 2)),
205 | (mBarLength + (mBarPointerHaloRadius)),
206 | (mBarPointerHaloRadius + (mBarThickness / 2)));
207 |
208 | // Update variables that depend of mBarLength.
209 | if (!isInEditMode()) {
210 | shader = new LinearGradient(
211 | mBarPointerHaloRadius,
212 | 0,
213 | (mBarLength + mBarPointerHaloRadius),
214 | mBarThickness,
215 | new int[] { Color.HSVToColor(0xFF, mHSVColor), Color.BLACK },
216 | null, Shader.TileMode.CLAMP);
217 | } else {
218 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
219 | (mBarLength + mBarPointerHaloRadius), mBarThickness,
220 | new int[] { 0xff81ff00, Color.BLACK }, null,
221 | Shader.TileMode.CLAMP);
222 | Color.colorToHSV(0xff81ff00, mHSVColor);
223 | }
224 |
225 | mBarPaint.setShader(shader);
226 | mPosToSatFactor = 1 / ((float) mBarLength);
227 | mSatToPosFactor = ((float) mBarLength) / 1;
228 |
229 | float[] hsvColor = new float[3];
230 | Color.colorToHSV(mColor, hsvColor);
231 |
232 | if (!isInEditMode()) {
233 | mBarPointerPosition = Math
234 | .round((mBarLength - (mSatToPosFactor * hsvColor[2]))
235 | + mBarPointerHaloRadius);
236 | } else {
237 | mBarPointerPosition = mBarPointerHaloRadius;
238 | }
239 | }
240 |
241 | @Override
242 | protected void onDraw(Canvas canvas) {
243 | // Draw the bar.
244 | canvas.drawRect(mBarRect, mBarPaint);
245 | // Draw the pointer halo.
246 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
247 | mBarPointerHaloRadius, mBarPointerHaloPaint);
248 | // Draw the pointer.
249 | canvas.drawCircle(mBarPointerPosition, mBarPointerHaloRadius,
250 | mBarPointerRadius, mBarPointerPaint);
251 | };
252 |
253 | @Override
254 | public boolean onTouchEvent(MotionEvent event) {
255 | getParent().requestDisallowInterceptTouchEvent(true);
256 |
257 | // Convert coordinates to our internal coordinate system
258 | float x = event.getX();
259 |
260 | switch (event.getAction()) {
261 | case MotionEvent.ACTION_DOWN:
262 | mIsMovingPointer = true;
263 | // Check whether the user pressed on (or near) the pointer
264 | if (x >= (mBarPointerHaloRadius)
265 | && x <= (mBarPointerHaloRadius + mBarLength)) {
266 | mBarPointerPosition = Math.round(x);
267 | calculateColor(Math.round(x));
268 | mBarPointerPaint.setColor(mColor);
269 | invalidate();
270 | }
271 | break;
272 | case MotionEvent.ACTION_MOVE:
273 | if (mIsMovingPointer) {
274 | // Move the the pointer on the bar.
275 | if (x >= mBarPointerHaloRadius
276 | && x <= (mBarPointerHaloRadius + mBarLength)) {
277 | mBarPointerPosition = Math.round(x);
278 | calculateColor(Math.round(x));
279 | mBarPointerPaint.setColor(mColor);
280 | if (mPicker != null) {
281 | mPicker.setNewCenterColor(mColor);
282 | mPicker.changeOpacityBarColor(mColor);
283 | }
284 | invalidate();
285 | } else if (x < mBarPointerHaloRadius) {
286 | mBarPointerPosition = mBarPointerHaloRadius;
287 | mColor = Color.HSVToColor(mHSVColor);
288 | mBarPointerPaint.setColor(mColor);
289 | if (mPicker != null) {
290 | mPicker.setNewCenterColor(mColor);
291 | mPicker.changeOpacityBarColor(mColor);
292 | }
293 | invalidate();
294 | } else if (x > (mBarPointerHaloRadius + mBarLength)) {
295 | mBarPointerPosition = mBarPointerHaloRadius + mBarLength;
296 | mColor = Color.BLACK;
297 | mBarPointerPaint.setColor(mColor);
298 | if (mPicker != null) {
299 | mPicker.setNewCenterColor(mColor);
300 | mPicker.changeOpacityBarColor(mColor);
301 | }
302 | invalidate();
303 | }
304 | }
305 | break;
306 | case MotionEvent.ACTION_UP:
307 | mIsMovingPointer = false;
308 | break;
309 | }
310 | return true;
311 | }
312 |
313 | /**
314 | * Set the bar color.
315 | *
316 | * Its discouraged to use this method.
317 | *
318 | * @param color
319 | */
320 | public void setColor(int color) {
321 | Color.colorToHSV(color, mHSVColor);
322 | shader = new LinearGradient(mBarPointerHaloRadius, 0,
323 | (mBarLength + mBarPointerHaloRadius), mBarThickness, new int[] {
324 | color, Color.BLACK }, null, Shader.TileMode.CLAMP);
325 | mBarPaint.setShader(shader);
326 | calculateColor(mBarPointerPosition);
327 | mBarPointerPaint.setColor(mColor);
328 | if (mPicker != null) {
329 | mPicker.setNewCenterColor(mColor);
330 | mPicker.changeOpacityBarColor(mColor);
331 | }
332 | invalidate();
333 | }
334 |
335 | /**
336 | * Set the pointer on the bar. With the opacity value.
337 | *
338 | * @param value
339 | * float between 0 > 1
340 | */
341 | public void setValue(float value) {
342 | mBarPointerPosition = Math
343 | .round((mBarLength - (mSatToPosFactor * value))
344 | + mBarPointerHaloRadius);
345 | calculateColor(mBarPointerPosition);
346 | mBarPointerPaint.setColor(mColor);
347 | if (mPicker != null) {
348 | mPicker.setNewCenterColor(mColor);
349 | mPicker.changeOpacityBarColor(mColor);
350 | }
351 | invalidate();
352 | }
353 |
354 | /**
355 | * Calculate the color selected by the pointer on the bar.
356 | *
357 | * @param x
358 | * X-Coordinate of the pointer.
359 | */
360 | private void calculateColor(int x) {
361 | x = x - mBarPointerHaloRadius;
362 | if (x < 0) {
363 | x = 0;
364 | } else if (x > mBarLength) {
365 | x = mBarLength;
366 | }
367 | mColor = Color.HSVToColor(new float[] { mHSVColor[0],
368 | mHSVColor[1],
369 | (float) (1 - (mPosToSatFactor * x)) });
370 | }
371 |
372 | /**
373 | * Get the currently selected color.
374 | *
375 | * @return The ARGB value of the currently selected color.
376 | */
377 | public int getColor() {
378 | return mColor;
379 | }
380 |
381 | /**
382 | * Adds a {@code ColorPicker} instance to the bar.
383 | *
384 | * WARNING: Don't change the color picker. it is done already when the bar
385 | * is added to the ColorPicker
386 | *
387 | * @see ColorPicker#addSVBar(SVBar)
388 | * @param picker
389 | */
390 | public void setColorPicker(ColorPicker picker) {
391 | mPicker = picker;
392 | }
393 |
394 | @Override
395 | protected Parcelable onSaveInstanceState() {
396 | Parcelable superState = super.onSaveInstanceState();
397 |
398 | Bundle state = new Bundle();
399 | state.putParcelable(STATE_PARENT, superState);
400 | state.putFloatArray(STATE_COLOR, mHSVColor);
401 |
402 | float[] hsvColor = new float[3];
403 | Color.colorToHSV(mColor, hsvColor);
404 | state.putFloat(STATE_VALUE, hsvColor[2]);
405 |
406 | return state;
407 | }
408 |
409 | @Override
410 | protected void onRestoreInstanceState(Parcelable state) {
411 | Bundle savedState = (Bundle) state;
412 |
413 | Parcelable superState = savedState.getParcelable(STATE_PARENT);
414 | super.onRestoreInstanceState(superState);
415 |
416 | setColor(Color.HSVToColor(savedState.getFloatArray(STATE_COLOR)));
417 | setValue(savedState.getFloat(STATE_VALUE));
418 | }
419 | }
420 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/App.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle;
2 |
3 | import android.app.Application;
4 | import eu.inmite.android.gridwichterle.core.Config;
5 |
6 | /**
7 | * Created with IntelliJ IDEA.
8 | * User: Michal Matl (michal.matl@inmite.eu)
9 | * Date: 10/12/13
10 | * Time: 10:53 PM
11 | */
12 | public class App extends Application {
13 |
14 | private Config mConfig;
15 |
16 | @Override
17 | public Object getSystemService(String name) {
18 |
19 | if (Config.class.getName().equals(name)) {
20 | if (mConfig == null) {
21 | mConfig = new Config(getApplicationContext());
22 | }
23 |
24 | return mConfig;
25 | }
26 |
27 | return super.getSystemService(name);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import eu.inmite.android.gridwichterle.services.GridOverlayService;
7 | import eu.inmite.android.gridwichterle.R;
8 |
9 |
10 | public class MainActivity extends Activity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_main);
16 |
17 | Intent intent = new Intent(this, GridOverlayService.class);
18 | startService(intent);
19 |
20 | finish();
21 | }
22 |
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/activity/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.activity;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.PackageInfo;
7 | import android.os.Bundle;
8 | import android.support.v4.app.FragmentActivity;
9 | import android.util.Log;
10 | import android.view.View;
11 | import android.widget.*;
12 |
13 | import eu.inmite.android.gridwichterle.R;
14 | import eu.inmite.android.gridwichterle.bus.BusProvider;
15 | import eu.inmite.android.gridwichterle.bus.CancelGridBus;
16 | import eu.inmite.android.gridwichterle.bus.ColorChangeBus;
17 | import eu.inmite.android.gridwichterle.bus.GridOnOffBus;
18 | import eu.inmite.android.gridwichterle.core.Config;
19 | import eu.inmite.android.gridwichterle.core.Constants;
20 | import eu.inmite.android.gridwichterle.core.Utils;
21 | import eu.inmite.android.gridwichterle.dialogs.ColorsDialog;
22 | import eu.inmite.android.gridwichterle.services.GridOverlayService;
23 |
24 | import butterknife.InjectView;
25 | import butterknife.Views;
26 | import com.squareup.otto.Subscribe;
27 |
28 | /**
29 | * Created with IntelliJ IDEA.
30 | * User: Michal Matl
31 | * Date: 21.10.13
32 | * Time: 20:10
33 | */
34 | public class SettingsActivity extends FragmentActivity {
35 |
36 | @InjectView(R.id.txtGridSize)
37 | public TextView txtGridSize;
38 | @InjectView(R.id.txtVersion)
39 | public TextView txtVersion;
40 | @InjectView(R.id.txtSendFeedback)
41 | public TextView txtSendFeedback;
42 | @InjectView(R.id.txtTheCode)
43 | public LinearLayout txtTheCode;
44 | @InjectView(R.id.seekBar)
45 | public SeekBar seekBar;
46 | @InjectView(R.id.layoutColor)
47 | public RelativeLayout layoutColor;
48 | @InjectView(R.id.viewColor)
49 | public View viewColor;
50 | @InjectView(R.id.chckFullScreen)
51 | public CheckedTextView chckFullScreen;
52 | @InjectView(R.id.layoutDevelopers)
53 | public LinearLayout layoutDevelopers;
54 | @InjectView(R.id.gridSwitch)
55 | public Switch switchGrid;
56 |
57 | private Config mConfig;
58 |
59 |
60 | public static void call(Context context) {
61 | Intent intent = new Intent(context, SettingsActivity.class);
62 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
63 | context.startActivity(intent);
64 | }
65 |
66 | @Override
67 | protected void onCreate(Bundle savedInstanceState) {
68 | super.onCreate(savedInstanceState);
69 | setContentView(R.layout.activity_settings);
70 | Views.inject(this);
71 |
72 | Intent intent = new Intent(this, GridOverlayService.class);
73 | startService(intent);
74 |
75 | setupViews();
76 | }
77 |
78 | private void setupViews() {
79 |
80 | mConfig = (Config)getApplicationContext().getSystemService(Config.class.getName());
81 | final String seekBarString = getString(R.string.settings_seek_bar);
82 |
83 | chckFullScreen.setChecked(mConfig.isFullScreenModeActivated());
84 | chckFullScreen.setOnClickListener(new View.OnClickListener() {
85 | @Override
86 | public void onClick(View view) {
87 | if (chckFullScreen.isChecked()) {
88 | chckFullScreen.setChecked(false);
89 | mConfig.setFullScreenMode(false);
90 | } else {
91 | chckFullScreen.setChecked(true);
92 | mConfig.setFullScreenMode(true);
93 | }
94 | applyNow();
95 | }
96 | });
97 |
98 | layoutDevelopers.setOnClickListener(new View.OnClickListener() {
99 | @Override
100 | public void onClick(View view) {
101 | Utils.openBrowser(SettingsActivity.this, "https://plus.google.com/110778431549186951626");
102 | }
103 | });
104 |
105 | txtTheCode.setOnClickListener(new View.OnClickListener() {
106 | @Override
107 | public void onClick(View view) {
108 | Utils.openBrowser(SettingsActivity.this, "https://github.com/inmite/android-grid-wichterle");
109 | }
110 | });
111 |
112 | txtSendFeedback.setOnClickListener(new View.OnClickListener() {
113 | @Override
114 | public void onClick(View view) {
115 | Utils.sendEmail(SettingsActivity.this, "android@inmite.eu");
116 | }
117 | });
118 |
119 | txtVersion.setText(getVersionName(this));
120 |
121 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
122 | @Override
123 | public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
124 | txtGridSize.setText(String.format(seekBarString, Integer.toString(progress + 4)));
125 | }
126 |
127 | @Override
128 | public void onStartTrackingTouch(SeekBar seekBar) {
129 |
130 | }
131 |
132 | @Override
133 | public void onStopTrackingTouch(SeekBar seekBar) {
134 | mConfig.setGridSideSize(seekBar.getProgress() + 4);
135 | applyNow();
136 | }
137 | });
138 |
139 | seekBar.setProgress(mConfig.getGridSideSize());
140 | txtGridSize.setText(String.format(seekBarString, Integer.toString(mConfig.getGridSideSize())));
141 |
142 | layoutColor.setOnClickListener(new View.OnClickListener() {
143 | @Override
144 | public void onClick(View view) {
145 | ColorsDialog.show(getSupportFragmentManager());
146 | }
147 | });
148 |
149 | viewColor.setBackgroundColor(mConfig.getColor());
150 |
151 | switchGrid.setChecked(GridOverlayService.sIsGridShown);
152 | switchGrid.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
153 | @Override
154 | public void onCheckedChanged(CompoundButton compoundButton, boolean switchOn) {
155 | if (switchOn) {
156 | BusProvider.getInstance().post(new GridOnOffBus(GridOnOffBus.ACTION_GRID_ON));
157 | } else {
158 | BusProvider.getInstance().post(new GridOnOffBus(GridOnOffBus.ACTION_GRID_OFF));
159 | }
160 | }
161 | });
162 |
163 | }
164 |
165 | private void applyNow() {
166 | if (switchGrid.isChecked()) {
167 | BusProvider.getInstance().post(new GridOnOffBus(GridOnOffBus.ACTION_GRID_OFF));
168 | BusProvider.getInstance().post(new GridOnOffBus(GridOnOffBus.ACTION_GRID_ON));
169 | }
170 | }
171 |
172 | private String getVersionName(Context ctx) {
173 | try {
174 | ComponentName comp = new ComponentName(ctx, ctx.getClass());
175 | PackageInfo pinfo = ctx.getPackageManager().getPackageInfo(comp.getPackageName(), 0);
176 | return pinfo.versionName;
177 | } catch (android.content.pm.PackageManager.NameNotFoundException e) {
178 | return "unknown";
179 | }
180 | }
181 |
182 | @Subscribe
183 | public void changeColor(ColorChangeBus colorChangeBus) {
184 | viewColor.setBackgroundColor(mConfig.getColor());
185 | applyNow();
186 | }
187 |
188 | @Subscribe
189 | public void cancelGrid(CancelGridBus cancelGridBus) {
190 | Log.d(Constants.TAG, "GridOverlayService.CancelGrid()");
191 | finish();
192 | }
193 |
194 | @Override
195 | protected void onStart() {
196 | super.onStart();
197 | BusProvider.getInstance().register(this);
198 | }
199 |
200 | @Override
201 | protected void onDestroy() {
202 | super.onDestroy();
203 | BusProvider.getInstance().unregister(this);
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/bus/BusProvider.java:
--------------------------------------------------------------------------------
1 | /*
* Copyright (C) 2012 Square, Inc.
*
* 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 eu.inmite.android.gridwichterle.bus;
import com.squareup.otto.Bus;
/**
* Maintains a singleton instance for obtaining the bus. Ideally this would be replaced with a more efficient means
* such as through injection directly into interested classes.
*/
public final class BusProvider {
private static final Bus BUS = new Bus();
public static Bus getInstance() {
return BUS;
}
private BusProvider() {
// No instances.
}
}
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/bus/CancelGridBus.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.bus;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: Michal Matl (michal.matl@inmite.eu)
6 | * Date: 7/21/13
7 | * Time: 9:19 PM
8 | */
9 | public class CancelGridBus {
10 | }
11 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/bus/ColorChangeBus.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.bus;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: Michal
6 | * Date: 28.10.13
7 | * Time: 15:14
8 | * To change this template use File | Settings | File Templates.
9 | */
10 | public class ColorChangeBus {
11 | }
12 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/bus/GridOnOffBus.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.bus;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: Michal Matl (michal.matl@inmite.eu)
6 | * Date: 10/13/13
7 | * Time: 7:05 PM
8 | */
9 | public class GridOnOffBus {
10 |
11 | public static final int ACTION_GRID_ON = 0;
12 | public static final int ACTION_GRID_OFF = 1;
13 |
14 | private int mAction;
15 |
16 | public GridOnOffBus(int action) {
17 | mAction = action;
18 | }
19 |
20 | public int getAction() {
21 | return mAction;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/bus/ShowSettingsBus.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.bus;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: Michal Matl (michal.matl@inmite.eu)
6 | * Date: 2.11.13
7 | * Time: 15:56
8 | */
9 | public class ShowSettingsBus {
10 | }
11 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/core/Config.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.core;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.graphics.Color;
7 |
8 | /**
9 | * Created with IntelliJ IDEA.
10 | * User: Michal Matl (michal.matl@inmite.eu)
11 | * Date: 10/12/13
12 | * Time: 10:36 PM
13 | */
14 | public class Config {
15 |
16 | private final String SHARED_CONFIG = "config";
17 |
18 | private Context mContext;
19 |
20 | public Config(Context context) {
21 | mContext = context;
22 | }
23 |
24 | private SharedPreferences getPrefs() {
25 | return mContext.getSharedPreferences(SHARED_CONFIG, Activity.MODE_PRIVATE);
26 | }
27 |
28 | public void setFullScreenMode(boolean fullScreenMode) {
29 | getPrefs().edit()
30 | .putBoolean("fullscreen", fullScreenMode)
31 | .apply();
32 | }
33 |
34 | public boolean isFullScreenModeActivated() {
35 | return getPrefs().getBoolean("fullscreen", false);
36 | }
37 |
38 | public void setGridSideSize(int side) {
39 | getPrefs().edit()
40 | .putInt("side_size", side)
41 | .apply();
42 | }
43 |
44 | public int getGridSideSize() {
45 | return getPrefs().getInt("side_size", Constants.DEFAULT_SQUARE_SIDE);
46 | }
47 |
48 | public void setColor(int color) {
49 | getPrefs().edit()
50 | .putInt("color", color)
51 | .apply();
52 | }
53 |
54 | public int getColor() {
55 | return getPrefs().getInt("color", Color.BLUE);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/core/Constants.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.core;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: Michal Matl (michal.matl@inmite.eu)
6 | * Date: 7/19/13
7 | * Time: 12:12 PM
8 | */
9 | public class Constants {
10 |
11 | public static final String TAG = "GridWichterle";
12 |
13 | public static final int SEEK_BAR_STEP_SIZE = 4;
14 | public static final int DEFAULT_SQUARE_SIDE = 8;
15 | }
16 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/core/NotificationReceiver.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.core;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.util.Log;
7 |
8 | import eu.inmite.android.gridwichterle.bus.CancelGridBus;
9 | import eu.inmite.android.gridwichterle.bus.BusProvider;
10 | import eu.inmite.android.gridwichterle.bus.GridOnOffBus;
11 | import eu.inmite.android.gridwichterle.bus.ShowSettingsBus;
12 |
13 | /**
14 | * Created with IntelliJ IDEA.
15 | * User: Michal Matl (michal.matl@inmite.eu)
16 | * Date: 7/21/13
17 | * Time: 9:00 PM
18 | */
19 | public class NotificationReceiver extends BroadcastReceiver {
20 |
21 | @Override
22 | public void onReceive(Context context, Intent intent) {
23 |
24 | String action = intent.getAction();
25 |
26 | if ("notification_cancelled".equals(action)) {
27 | BusProvider.getInstance().post(new CancelGridBus());
28 | }
29 |
30 | if ("notification_settings".equals(action)) {
31 | Log.d(Constants.TAG, "Settings on");
32 | BusProvider.getInstance().post(new ShowSettingsBus());
33 | }
34 |
35 | if ("notification_settings_off".equals(action)) {
36 | BusProvider.getInstance().post(new GridOnOffBus(GridOnOffBus.ACTION_GRID_OFF));
37 | }
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/core/Utils.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.core;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.text.TextUtils;
7 | import android.util.TypedValue;
8 |
9 | /**
10 | * Created with IntelliJ IDEA.
11 | * User: Michal Matl (michal.matl@inmite.eu)
12 | * Date: 7/20/13
13 | * Time: 11:00 PM
14 | */
15 | public class Utils {
16 |
17 | public static int getStatusBarHeight(Context context) {
18 | int result = 0;
19 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
20 | if (resourceId > 0) {
21 | result = context.getResources().getDimensionPixelSize(resourceId);
22 | }
23 | return result;
24 | }
25 |
26 | public static int getPxFromDpi(Context context, int px) {
27 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) px, context.getResources().getDisplayMetrics());
28 | }
29 |
30 | /**
31 | * Opens external browser, e.g. Chrome.
32 | */
33 | public static void openBrowser(Context context, String url) {
34 | context.startActivity(createOpenBrowserIntent(url));
35 | }
36 |
37 | /**
38 | * Creates intent for opening browser.
39 | */
40 | public static Intent createOpenBrowserIntent(String url) {
41 | if (!url.startsWith("http")) {
42 | url = "http://" + url;
43 | }
44 | return new Intent(Intent.ACTION_VIEW, Uri.parse(url));
45 | }
46 |
47 | /**
48 | * Opens e-mail client, e.g. Gmail.
49 | */
50 | public static void sendEmail(Context context, String recipient) {
51 | sendEmail(context, new String[]{recipient}, null, null, null);
52 | }
53 |
54 | /**
55 | * Opens e-mail client e.g. Gmail.
56 | */
57 | public static void sendEmail(Context context, String[] recipients, String subject, String text, Uri stream) {
58 | context.startActivity(createSendEmailIntent(recipients, subject, text, stream));
59 | }
60 |
61 | /**
62 | * Creates intent for sending e-mail.
63 | */
64 | public static Intent createSendEmailIntent(String[] recipients, String subject, String text, Uri stream) {
65 | Intent i = new Intent(Intent.ACTION_SEND);
66 | i.setType("message/rfc822");
67 | if (recipients != null) {
68 | i.putExtra(Intent.EXTRA_EMAIL, recipients);
69 | }
70 | i.putExtra(Intent.EXTRA_SUBJECT, subject);
71 | if (!TextUtils.isEmpty(text)) {
72 | i.putExtra(Intent.EXTRA_TEXT, text);
73 | }
74 | if (stream != null) {
75 | i.putExtra(Intent.EXTRA_STREAM, stream);
76 | }
77 | return i;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/dialogs/ColorsDialog.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.dialogs;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.DialogInterface;
6 | import android.graphics.Color;
7 | import android.os.Bundle;
8 | import android.support.v4.app.DialogFragment;
9 | import android.support.v4.app.Fragment;
10 | import android.support.v4.app.FragmentManager;
11 | import android.support.v4.app.FragmentTransaction;
12 | import android.util.Log;
13 | import android.view.LayoutInflater;
14 | import android.view.View;
15 | import android.widget.Button;
16 |
17 | import eu.inmite.android.gridwichterle.R;
18 | import eu.inmite.android.gridwichterle.bus.BusProvider;
19 | import eu.inmite.android.gridwichterle.bus.ColorChangeBus;
20 | import eu.inmite.android.gridwichterle.core.Config;
21 | import eu.inmite.android.gridwichterle.core.Constants;
22 |
23 | import com.larswerkman.colorpicker.ColorPicker;
24 | import com.larswerkman.colorpicker.OpacityBar;
25 |
26 | /**
27 | * Created with IntelliJ IDEA.
28 | * User: Michal
29 | * Date: 27.10.13
30 | * Time: 22:12
31 | */
32 | public class ColorsDialog extends DialogFragment {
33 |
34 | public static Fragment show(FragmentManager fragmentManager) {
35 | try {
36 | FragmentTransaction ft = fragmentManager.beginTransaction();
37 | ColorsDialog fragment = (ColorsDialog)fragmentManager.findFragmentByTag("ColorDialog");
38 |
39 | if (fragment != null) {
40 | Log.v(Constants.TAG, "ColorDialog.showDialog() - found and close existing ColorDialog");
41 | ft.remove(fragment);
42 | }
43 |
44 | fragment = new ColorsDialog();
45 |
46 | fragment.show(ft, "ColorDialog");
47 | Log.v(Constants.TAG, "ColorDialog.showDialog() - show dialog was called");
48 |
49 | return fragment;
50 | } catch (Exception ex) {
51 | Log.wtf("ColorDialog.showDialog() - failed", ex);
52 | }
53 | return null;
54 | }
55 |
56 | @Override
57 | public Dialog onCreateDialog(Bundle savedInstanceState) {
58 | final Config config = (Config)getActivity().getApplicationContext().getSystemService(Config.class.getName());
59 | View view = getContentView();
60 |
61 | final ColorPicker picker = (ColorPicker)view.findViewById(R.id.picker);
62 | picker.setOldCenterColor(config.getColor());
63 | picker.setColor(config.getColor());
64 |
65 | OpacityBar opacityBar = (OpacityBar)view.findViewById(R.id.opacitybar);
66 | picker.addOpacityBar(opacityBar);
67 |
68 | Button btnWhite = (Button) view.findViewById(R.id.btnWhite);
69 | btnWhite.setOnClickListener(new View.OnClickListener() {
70 | @Override
71 | public void onClick(View view) {
72 | config.setColor(Color.WHITE);
73 | BusProvider.getInstance().post(new ColorChangeBus());
74 | dismiss();
75 | }
76 | });
77 |
78 | Button btnBlack = (Button) view.findViewById(R.id.btnBlack);
79 | btnBlack.setOnClickListener(new View.OnClickListener() {
80 | @Override
81 | public void onClick(View view) {
82 | config.setColor(Color.BLACK);
83 | BusProvider.getInstance().post(new ColorChangeBus());
84 | dismiss();
85 | }
86 | });
87 |
88 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
89 | builder.setView(view);
90 |
91 |
92 | builder.setTitle(R.string.dialog_title_choose_color);
93 | builder.setPositiveButton(R.string.dialog_set_color,
94 | new DialogInterface.OnClickListener() {
95 | @Override
96 | public void onClick(DialogInterface dialog, int which) {
97 | config.setColor(picker.getColor());
98 | BusProvider.getInstance().post(new ColorChangeBus());
99 | }
100 | });
101 |
102 | builder.setNegativeButton(R.string.dialog_cancel,
103 | new DialogInterface.OnClickListener() {
104 | @Override
105 | public void onClick(DialogInterface dialog, int which) {
106 | dialog.cancel();
107 | }
108 | });
109 | Dialog dialog = builder.create();
110 | return dialog;
111 | }
112 |
113 | private View getContentView() {
114 | LayoutInflater inflater = getActivity().getLayoutInflater();
115 | return inflater.inflate(R.layout.dialog_settings_color, null);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/services/GridOverlayService.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.services;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationManager;
5 | import android.app.PendingIntent;
6 | import android.app.Service;
7 | import android.content.Intent;
8 | import android.content.res.Configuration;
9 | import android.graphics.PixelFormat;
10 | import android.os.IBinder;
11 | import android.support.v4.app.NotificationCompat;
12 | import android.util.Log;
13 | import android.view.ViewGroup;
14 | import android.view.WindowManager;
15 | import android.widget.RemoteViews;
16 | import com.squareup.otto.Subscribe;
17 | import eu.inmite.android.gridwichterle.R;
18 | import eu.inmite.android.gridwichterle.activity.SettingsActivity;
19 | import eu.inmite.android.gridwichterle.bus.BusProvider;
20 | import eu.inmite.android.gridwichterle.bus.CancelGridBus;
21 | import eu.inmite.android.gridwichterle.bus.GridOnOffBus;
22 | import eu.inmite.android.gridwichterle.bus.ShowSettingsBus;
23 | import eu.inmite.android.gridwichterle.core.Constants;
24 | import eu.inmite.android.gridwichterle.core.NotificationReceiver;
25 | import eu.inmite.android.gridwichterle.views.GridOverlay;
26 |
27 | /**
28 | * Created with IntelliJ IDEA.
29 | * User: Michal Matl (michal.matl@inmite.eu)
30 | * Date: 7/19/13
31 | * Time: 11:22 AM
32 | */
33 | public class GridOverlayService extends Service {
34 |
35 | private GridOverlay mGridOverlay;
36 |
37 | private static final int NOTIFICATION_ID = 1;
38 |
39 | public static boolean sIsServiceRunning = false;
40 | public static boolean sIsGridShown = false;
41 |
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 |
47 | BusProvider.getInstance().register(this);
48 |
49 | }
50 |
51 | private void showGrid() {
52 | Log.d(Constants.TAG, "GridOverlayService.showGrid()");
53 | GridOverlayService.sIsGridShown = true;
54 | final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
55 | ViewGroup.LayoutParams.MATCH_PARENT,
56 | ViewGroup.LayoutParams.MATCH_PARENT,
57 | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
58 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
59 | PixelFormat.TRANSLUCENT
60 | );
61 |
62 | final WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
63 | mGridOverlay = new GridOverlay(this);
64 |
65 | wm.addView(mGridOverlay, lp);
66 | }
67 |
68 | private void removeGrid() {
69 | Log.d(Constants.TAG, "GridOverlayService.removeGrid()");
70 | GridOverlayService.sIsGridShown = false;
71 | try {
72 | if(mGridOverlay != null && mGridOverlay.getParent() != null) {
73 | final WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
74 | wm.removeView(mGridOverlay);
75 | }
76 | } catch (Exception e) {
77 | Log.e(Constants.TAG, "Remove grid failed");
78 | }
79 | }
80 |
81 | private void removeNotification() {
82 |
83 | NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
84 | notificationManager.cancel(NOTIFICATION_ID);
85 | }
86 |
87 | private void restartGrid() {
88 | Log.d(Constants.TAG, "GridOverlayService.restartGrid()");
89 | removeGrid();
90 | showGrid();
91 | }
92 |
93 | @Subscribe
94 | public void showSettings(ShowSettingsBus showSettingsBus) {
95 | Log.d(Constants.TAG, "GridOverlayService.showSettings()");
96 | SettingsActivity.call(getApplicationContext());
97 | }
98 |
99 | private void showNotification() {
100 | Log.d(Constants.TAG, "GridOverlayService.showNotification()");
101 |
102 | // custom notification layout
103 | RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification_layout);
104 | contentView.setOnClickPendingIntent(R.id.btnSettings, getDeleteIntent());
105 |
106 | final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
107 | builder.setSmallIcon(R.drawable.ic_launcher)
108 | .setOngoing(false)
109 | .setAutoCancel(true)
110 | .setContent(contentView)
111 | .setContentIntent(getSettingIntent());
112 | Notification notification = builder.getNotification();
113 | startForeground(NOTIFICATION_ID, notification);
114 |
115 | /*
116 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
117 | final Notification.Builder builder = new Notification.Builder(this);
118 | builder.setSmallIcon(R.drawable.ic_launcher)
119 | .setContentTitle(getString(R.string.notification_title))
120 | .setContentText(getString(R.string.notification_desc))
121 | .setContentIntent(getSettingIntent())
122 | .setStyle(new Notification.BigTextStyle().bigText(getString(R.string.notification_desc)))
123 | .addAction(R.drawable.ic_remove, getString(R.string.cancel_grid), getDeleteIntent());
124 |
125 | startForeground(NOTIFICATION_ID, builder.build());
126 | } else {
127 | // custom notification layout for an older version
128 | RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification_layout);
129 | contentView.setOnClickPendingIntent(R.id.btnSettings, getDeleteIntent());
130 |
131 | final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
132 | builder.setSmallIcon(R.drawable.ic_launcher)
133 | .setOngoing(false)
134 | .setAutoCancel(true)
135 | .setContent(contentView)
136 | .setContentIntent(getSettingIntent());
137 | Notification notification = builder.getNotification();
138 | startForeground(NOTIFICATION_ID, notification);
139 | }*/
140 | }
141 |
142 | private PendingIntent getDeleteIntent() {
143 | Intent intent = new Intent(getApplicationContext(), NotificationReceiver.class);
144 | intent.setAction("notification_cancelled");
145 | return PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
146 | }
147 |
148 | private PendingIntent getSettingIntent() {
149 | Intent intent = new Intent(getApplicationContext(), NotificationReceiver.class);
150 | intent.setAction("notification_settings");
151 | return PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
152 | }
153 |
154 | private PendingIntent getSettingOffIntent() {
155 | Intent intent = new Intent(getApplicationContext(), NotificationReceiver.class);
156 | intent.setAction("notification_settings_off");
157 | return PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
158 | }
159 |
160 | @Subscribe
161 | public void cancelGrid(CancelGridBus cancelGridBus) {
162 | Log.d(Constants.TAG, "GridOverlayService.CancelGrid()");
163 |
164 | sIsServiceRunning = false;
165 | sIsGridShown = false;
166 |
167 | removeGrid();
168 | stopForeground(true);
169 | }
170 |
171 | @Subscribe
172 | public void gridAction(GridOnOffBus settingsOnOffBus) {
173 |
174 | if (settingsOnOffBus.getAction() == GridOnOffBus.ACTION_GRID_ON) {
175 | showGrid();
176 | }
177 |
178 | if (settingsOnOffBus.getAction() == GridOnOffBus.ACTION_GRID_OFF) {
179 | removeGrid();
180 | }
181 |
182 | }
183 |
184 | @Override
185 | public int onStartCommand(Intent intent, int flags, int startId) {
186 | if(!sIsServiceRunning) {
187 | sIsServiceRunning = true;
188 | showNotification();
189 | }
190 | return Service.START_NOT_STICKY;
191 | }
192 |
193 | @Override
194 | public void onConfigurationChanged(Configuration newConfig) {
195 | super.onConfigurationChanged(newConfig);
196 | //we need restart grid when a screen rotates
197 | if (mGridOverlay != null && mGridOverlay.getParent() != null) {
198 | restartGrid();
199 | }
200 | }
201 |
202 | @Override
203 | public void onDestroy() {
204 | super.onDestroy();
205 |
206 | BusProvider.getInstance().unregister(this);
207 | }
208 |
209 | @Override
210 | public IBinder onBind(Intent intent) {
211 | return null;
212 | }
213 |
214 |
215 | }
216 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/views/DrawView.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.view.View;
7 | import eu.inmite.android.gridwichterle.core.Config;
8 | import eu.inmite.android.gridwichterle.core.Utils;
9 |
10 | import java.util.LinkedList;
11 | import java.util.List;
12 |
13 | /**
14 | * Created with IntelliJ IDEA.
15 | * User: Michal Matl (michal.matl@inmite.eu)
16 | * Date: 7/21/13
17 | * Time: 4:32 PM
18 | */
19 | public class DrawView extends View {
20 |
21 | private final Paint paint = new Paint();
22 | private final int height;
23 | private final int width;
24 | private final int mSquare;
25 | private float[] points;
26 |
27 | public DrawView(Context context, int height, int width) {
28 | super(context);
29 |
30 | this.height = height;
31 | this.width = width;
32 |
33 | Config config = (Config) getContext().getApplicationContext().getSystemService(Config.class.getName());
34 | paint.setColor(config.getColor());
35 | mSquare = Utils.getPxFromDpi(getContext(), config.getGridSideSize());
36 | }
37 |
38 | @Override
39 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
40 | super.onSizeChanged(w, h, oldw, oldh);
41 |
42 | List gridPoints = new LinkedList();
43 |
44 | int countHorizontalLines = height / mSquare;
45 | int countVerticalLines = width / mSquare;
46 |
47 | //prepare horizontal lines
48 | float gap = mSquare;
49 | for (int i = 0; i <= countHorizontalLines; i++) {
50 | gridPoints.add(0f);
51 | gridPoints.add(gap);
52 | gridPoints.add((float) width);
53 | gridPoints.add(gap);
54 |
55 | gap = gap + mSquare;
56 |
57 | }
58 |
59 | //prepare vertical lines
60 | gap = mSquare;
61 | for (int i = 0; i <= countVerticalLines; i++) {
62 | gridPoints.add(gap);
63 | gridPoints.add(0f);
64 | gridPoints.add(gap);
65 | gridPoints.add((float) height);
66 |
67 | gap = gap + mSquare;
68 | }
69 |
70 | points = new float[gridPoints.size()];
71 | for (int i = 0; i < gridPoints.size(); i++) {
72 | points[i] = gridPoints.get(i);
73 | }
74 | }
75 |
76 | @Override
77 | public void onDraw(Canvas canvas) {
78 | super.onDraw(canvas);
79 | //draw whole grid
80 | canvas.drawLines(points, paint);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/eu/inmite/android/gridwichterle/views/GridOverlay.java:
--------------------------------------------------------------------------------
1 | package eu.inmite.android.gridwichterle.views;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Point;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.view.WindowManager;
9 | import android.widget.FrameLayout;
10 | import android.widget.LinearLayout;
11 | import android.widget.RelativeLayout;
12 | import eu.inmite.android.gridwichterle.R;
13 | import eu.inmite.android.gridwichterle.core.Config;
14 | import eu.inmite.android.gridwichterle.core.Utils;
15 |
16 |
17 | /**
18 | * Created with IntelliJ IDEA.
19 | * User: Michal Matl (michal.matl@inmite.eu)
20 | * Date: 7/15/13
21 | * Time: 11:13 PM
22 | */
23 | public class GridOverlay extends RelativeLayout {
24 |
25 |
26 |
27 | public GridOverlay(Context context) {
28 | super(context);
29 | this.setId(R.id.grid_overlay);
30 | setupView(getDisplayHeight(context), getDisplayWidth(context));
31 | }
32 |
33 | private void setupView(int height, int width) {
34 |
35 | Config config = (Config)getContext().getApplicationContext().getSystemService(Config.class.getName());
36 |
37 | RelativeLayout.LayoutParams gridOverlayParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
38 |
39 | FrameLayout mGridHolder = new FrameLayout(getContext());
40 | if(!config.isFullScreenModeActivated()) {
41 | mGridHolder.setPadding(0, Utils.getStatusBarHeight(getContext()), 0, 0);
42 | }
43 |
44 | mGridHolder.setLayoutParams(gridOverlayParams);
45 | this.addView(mGridHolder);
46 |
47 | //DrawView
48 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
49 | DrawView mDrawView = new DrawView(getContext(), height, width);
50 | mDrawView.setLayoutParams(layoutParams);
51 |
52 | mGridHolder.addView(mDrawView);
53 | }
54 |
55 |
56 | public static void show(Activity activity) {
57 | GridOverlay gridOverlay = new GridOverlay(activity);
58 | ((ViewGroup) activity.getWindow().getDecorView().getRootView()).addView(gridOverlay);
59 | }
60 |
61 | public static boolean isVisible(Activity activity) {
62 | View view = (activity.getWindow().getDecorView().getRootView()).findViewById(R.id.grid_overlay);
63 | if (view == null) {
64 | return false;
65 | } else {
66 | return true;
67 | }
68 | }
69 |
70 | public static void remove(Activity activity) {
71 | View view = (activity.getWindow().getDecorView().getRootView()).findViewById(R.id.grid_overlay);
72 | if (view != null) {
73 | ((ViewGroup) activity.getWindow().getDecorView().getRootView()).removeView(view);
74 | }
75 | }
76 |
77 | private int getDisplayHeight(Context context) {
78 | Point size = new Point();
79 | final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
80 | wm.getDefaultDisplay().getSize(size);
81 | return size.y;
82 | }
83 |
84 | private int getDisplayWidth(Context context) {
85 | Point size = new Point();
86 | final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
87 | wm.getDefaultDisplay().getSize(size);
88 | return size.x;
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------