annotationMap = itemsMap.get(sensorType);
173 | AnnotatedMethod sensorChangedAnnotatedMethod = annotationMap.get(OnSensorChanged.class);
174 | AnnotatedMethod accuracyChangedAnnotatedMethod =
175 | annotationMap.get(OnAccuracyChanged.class);
176 | AnnotatedMethod triggerAnnotatedMethod = annotationMap.get(OnTrigger.class);
177 |
178 | if (sensorType == TYPE_SIGNIFICANT_MOTION && (accuracyChangedAnnotatedMethod != null
179 | || sensorChangedAnnotatedMethod != null)) {
180 | throw new ProcessingException(null, String.format(
181 | "@%s and @%s are not supported for the \"TYPE_SIGNIFICANT_MOTION\" type. Use @%s for this type.",
182 | OnSensorChanged.class.getSimpleName(), OnAccuracyChanged.class.getSimpleName(),
183 | OnTrigger.class.getSimpleName()));
184 | } else if (sensorType != TYPE_SIGNIFICANT_MOTION && triggerAnnotatedMethod != null) {
185 | throw new ProcessingException(null, String.format(
186 | "The @%s is only supported for the \"TYPE_SIGNIFICANT_MOTION\" type.",
187 | OnTrigger.class.getSimpleName()));
188 | }
189 |
190 | CodeBlock listenerWrapperCodeBlock;
191 | if (triggerAnnotatedMethod != null) {
192 | listenerWrapperCodeBlock = createTriggerListenerWrapper(triggerAnnotatedMethod);
193 | constructorBuilder.addCode(listenerWrapperCodeBlock);
194 | } else if (sensorChangedAnnotatedMethod != null
195 | || accuracyChangedAnnotatedMethod != null) {
196 | listenerWrapperCodeBlock =
197 | createSensorListenerWrapper(sensorType, sensorChangedAnnotatedMethod,
198 | accuracyChangedAnnotatedMethod);
199 | constructorBuilder.addCode(listenerWrapperCodeBlock);
200 | }
201 | }
202 |
203 | return constructorBuilder.build();
204 | }
205 |
206 | /**
207 | * Create an {@code EventListenerWrapper} that contains the {@code TriggerEventListener} and
208 | * calls the annotated methods on our target.
209 | *
210 | * @param triggerAnnotatedMethod Method annotated with {@link OnTrigger}.
211 | * @return {@link CodeBlock} of the {@code EventListenerWrapper}.
212 | */
213 | @NonNull
214 | private static CodeBlock createTriggerListenerWrapper(
215 | @NonNull AnnotatedMethod triggerAnnotatedMethod) throws ProcessingException {
216 | checkAnnotatedMethodForErrors(triggerAnnotatedMethod.getExecutableElement(),
217 | OnTrigger.class);
218 |
219 | CodeBlock listenerBlock = CodeBlock.builder()
220 | .add("new $T() {\n", TRIGGER_EVENT_LISTENER)
221 | .indent()
222 | .add(createOnTriggerListenerMethod(triggerAnnotatedMethod).toString())
223 | .unindent()
224 | .add("}")
225 | .build();
226 |
227 | return CodeBlock.builder()
228 | .addStatement("this.$N.add(new $T($L))", LISTENER_WRAPPERS_FIELD,
229 | TRIGGER_EVENT_LISTENER_WRAPPER, listenerBlock)
230 | .build();
231 | }
232 |
233 | /**
234 | * Create an {@code EventListenerWrapper} that contains the {@code
235 | * SensorEventListener} and calls the annotated methods on our target.
236 | *
237 | * @param sensorType The {@code Sensor} type.
238 | * @param sensorChangedAnnotatedMethod Method annotated with {@link OnSensorChanged}.
239 | * @param accuracyChangedAnnotatedMethod Method annotated with {@link OnAccuracyChanged}.
240 | * @return {@link CodeBlock} of the {@code EventListenerWrapper}.
241 | */
242 | @NonNull
243 | private static CodeBlock createSensorListenerWrapper(int sensorType,
244 | @Nullable AnnotatedMethod sensorChangedAnnotatedMethod,
245 | @Nullable AnnotatedMethod accuracyChangedAnnotatedMethod) throws ProcessingException {
246 | if (sensorChangedAnnotatedMethod != null) {
247 | checkAnnotatedMethodForErrors(sensorChangedAnnotatedMethod.getExecutableElement(),
248 | OnSensorChanged.class);
249 | }
250 | if (accuracyChangedAnnotatedMethod != null) {
251 | checkAnnotatedMethodForErrors(accuracyChangedAnnotatedMethod.getExecutableElement(),
252 | OnAccuracyChanged.class);
253 | }
254 |
255 | CodeBlock listenerBlock = CodeBlock.builder()
256 | .add("new $T() {\n", SENSOR_EVENT_LISTENER)
257 | .indent()
258 | .add(createOnSensorChangedListenerMethod(sensorChangedAnnotatedMethod).toString())
259 | .add(createOnAccuracyChangedListenerMethod(accuracyChangedAnnotatedMethod).toString())
260 | .unindent()
261 | .add("}")
262 | .build();
263 |
264 | int delay =
265 | getDelayForListener(sensorChangedAnnotatedMethod, accuracyChangedAnnotatedMethod);
266 |
267 | if (delay == INVALID_DELAY) {
268 | String error =
269 | String.format("@%s or @%s needs a delay value specified in the annotation",
270 | OnSensorChanged.class.getSimpleName(), OnAccuracyChanged.class.getSimpleName());
271 | throw new ProcessingException(null, error);
272 | }
273 |
274 | return CodeBlock.builder()
275 | .addStatement("this.$N.add(new $T($L, $L, $L))", LISTENER_WRAPPERS_FIELD,
276 | SENSOR_EVENT_LISTENER_WRAPPER, sensorType, delay, listenerBlock)
277 | .build();
278 | }
279 |
280 | /**
281 | * Creates the implementation of {@code TriggerEventListener#onTrigger(TriggerEvent)} which
282 | * calls the annotated method on our target class.
283 | *
284 | * @param annotatedMethod Method annotated with {@code OnTrigger}.
285 | * @return {@link MethodSpec} of {@code TriggerEventListener#onTrigger(TriggerEvent)}.
286 | */
287 | @NonNull
288 | private static MethodSpec createOnTriggerListenerMethod(
289 | @NonNull AnnotatedMethod annotatedMethod) {
290 | ParameterSpec triggerEventParameter = ParameterSpec.builder(TRIGGER_EVENT, "event").build();
291 | ExecutableElement triggerExecutableElement = annotatedMethod.getExecutableElement();
292 | return getBaseMethodBuilder("onTrigger").addParameter(triggerEventParameter)
293 | .addStatement("target.$L($N)", triggerExecutableElement.getSimpleName(),
294 | triggerEventParameter)
295 | .build();
296 | }
297 |
298 | /**
299 | * Creates the implementation of {@code SensorEventListener#onSensorChanged(SensorEvent)} which
300 | * calls the annotated method on our target class.
301 | *
302 | * @param annotatedMethod Method annotated with {@code OnSensorChanged}.
303 | * @return {@link MethodSpec} of {@code SensorEventListener#onSensorChanged(SensorEvent)}.
304 | */
305 | @NonNull
306 | private static MethodSpec createOnSensorChangedListenerMethod(
307 | @Nullable AnnotatedMethod annotatedMethod) {
308 | ParameterSpec sensorEventParameter = ParameterSpec.builder(SENSOR_EVENT, "event").build();
309 | Builder methodBuilder =
310 | getBaseMethodBuilder("onSensorChanged").addParameter(sensorEventParameter);
311 |
312 | if (annotatedMethod != null) {
313 | ExecutableElement sensorChangedExecutableElement =
314 | annotatedMethod.getExecutableElement();
315 | methodBuilder.addStatement("target.$L($N)",
316 | sensorChangedExecutableElement.getSimpleName(), sensorEventParameter);
317 | }
318 |
319 | return methodBuilder.build();
320 | }
321 |
322 | /**
323 | * Creates the implementation of {@code SensorEventListener#onAccuracyChanged(Sensor, int)}
324 | * which calls the annotated method on our target class.
325 | *
326 | * @param annotatedMethod Method annotated with {@link OnAccuracyChanged}.
327 | * @return {@link MethodSpec} of {@code SensorEventListener#onAccuracyChanged(Sensor, int)}.
328 | */
329 | @NonNull
330 | private static MethodSpec createOnAccuracyChangedListenerMethod(
331 | @Nullable AnnotatedMethod annotatedMethod) {
332 | ParameterSpec sensorParameter = ParameterSpec.builder(SENSOR, "sensor").build();
333 | ParameterSpec accuracyParameter = ParameterSpec.builder(TypeName.INT, "accuracy").build();
334 | Builder methodBuilder =
335 | getBaseMethodBuilder("onAccuracyChanged").addParameter(sensorParameter)
336 | .addParameter(accuracyParameter);
337 |
338 | if (annotatedMethod != null) {
339 | ExecutableElement accuracyChangedExecutableElement =
340 | annotatedMethod.getExecutableElement();
341 | methodBuilder.addStatement("target.$L($N, $N)",
342 | accuracyChangedExecutableElement.getSimpleName(), sensorParameter,
343 | accuracyParameter);
344 | }
345 |
346 | return methodBuilder.build();
347 | }
348 |
349 | /**
350 | * Returns a delay to be used when registering the listener for the sensor. Both {@link
351 | * OnSensorChanged} and {@link OnAccuracyChanged} can have
352 | * a delay property set but only one can be used when registering the listener.
353 | *
354 | * We try {@link OnSensorChanged} first, then {@link OnAccuracyChanged}, otherwise we return a
355 | * sentinel value that will be used for errors.
356 | *
357 | * @param sensorChangedAnnotatedMethod The method wrapper for the method with the {@link
358 | * OnSensorChanged} annotation.
359 | * @param accuracyChangedAnnotatedMethod The method wrapper for the method with the {@link
360 | * OnAccuracyChanged} annotation.
361 | * @return A delay value for the sensor listener.
362 | */
363 | private static int getDelayForListener(@Nullable AnnotatedMethod sensorChangedAnnotatedMethod,
364 | @Nullable AnnotatedMethod accuracyChangedAnnotatedMethod) {
365 | if (sensorChangedAnnotatedMethod != null
366 | && sensorChangedAnnotatedMethod.getDelay() != INVALID_DELAY) {
367 | return sensorChangedAnnotatedMethod.getDelay();
368 | } else if (accuracyChangedAnnotatedMethod != null
369 | && accuracyChangedAnnotatedMethod.getDelay() != INVALID_DELAY) {
370 | return accuracyChangedAnnotatedMethod.getDelay();
371 | }
372 |
373 | return INVALID_DELAY;
374 | }
375 |
376 | /**
377 | * Create the bind method for our generated class.
378 | *
379 | * @param targetParameter The target class that has annotated methods.
380 | * @param annotatedMethodsPerClass The annotated methods that are in a given class.
381 | * @return {@link MethodSpec} of the generated class bind method.
382 | */
383 | @NonNull
384 | private static MethodSpec createBindMethod(@NonNull ParameterSpec targetParameter,
385 | @NonNull AnnotatedMethodsPerClass annotatedMethodsPerClass) throws ProcessingException {
386 | Map> itemsMap = annotatedMethodsPerClass.getItemsMap();
387 |
388 | Builder bindMethodBuilder = getBaseMethodBuilder("bind").addParameter(targetParameter)
389 | .addStatement("int sensorType")
390 | .addStatement("$T sensor", SENSOR)
391 | .beginControlFlow("for ($T wrapper : $N)", LISTENER_WRAPPER, LISTENER_WRAPPERS_FIELD)
392 | .addStatement("sensorType = wrapper.getSensorType()")
393 | .addStatement("sensor = wrapper.getSensor($N)", SENSOR_MANAGER_FIELD);
394 |
395 | if (annotatedMethodsPerClass.hasAnnotationsOfType(OnSensorNotAvailable.class)) {
396 | bindMethodBuilder.beginControlFlow("if (sensor == null)");
397 |
398 | // Iterate through our map of sensor types and check whether an OnSensorNotAvailable
399 | // annotation exists, if so and the sensor is unavailable call the method.
400 | List sensorTypes = new ArrayList<>();
401 | sensorTypes.addAll(itemsMap.keySet());
402 | for (int i = 0; i < sensorTypes.size(); i++) {
403 | Integer sensorType = sensorTypes.get(i);
404 | Map annotationMap = itemsMap.get(sensorType);
405 | AnnotatedMethod annotatedMethod = annotationMap.get(OnSensorNotAvailable.class);
406 |
407 | if (annotatedMethod != null) {
408 | checkAnnotatedMethodForErrors(annotatedMethod.getExecutableElement(),
409 | OnSensorNotAvailable.class);
410 |
411 | if (i == 0) {
412 | bindMethodBuilder.beginControlFlow("if (sensorType == $L)", sensorType);
413 | } else {
414 | bindMethodBuilder.nextControlFlow("else if (sensorType == $L)", sensorType);
415 | }
416 |
417 | bindMethodBuilder.addStatement("$N.$L()", targetParameter,
418 | annotatedMethod.getExecutableElement().getSimpleName());
419 | }
420 | }
421 |
422 | bindMethodBuilder.endControlFlow().addStatement("continue").endControlFlow();
423 | }
424 |
425 | return bindMethodBuilder.addStatement("wrapper.registerListener($N)", SENSOR_MANAGER_FIELD)
426 | .endControlFlow()
427 | .build();
428 | }
429 |
430 | /**
431 | * Return a {@link Builder} with the given method name and default properties.
432 | *
433 | * @param name The name of the method.
434 | * @return A base {@link Builder} to use for methods.
435 | */
436 | @NonNull
437 | private static Builder getBaseMethodBuilder(@NonNull String name) {
438 | return MethodSpec.methodBuilder(name)
439 | .addModifiers(Modifier.PUBLIC)
440 | .returns(void.class)
441 | .addAnnotation(Override.class);
442 | }
443 |
444 | /**
445 | * Check the annotated method for the correct parameters needed based on the annotation.
446 | *
447 | * @param element The annotated element.
448 | * @param annotation The annotation class being checked.
449 | * @throws ProcessingException
450 | */
451 | private static void checkAnnotatedMethodForErrors(@NonNull ExecutableElement element,
452 | @NonNull Class extends Annotation> annotation) throws ProcessingException {
453 | ListenerMethod method = annotation.getAnnotation(ListenerMethod.class);
454 | String[] expectedParameters = method.parameters();
455 | List extends VariableElement> parameters = element.getParameters();
456 | if (parameters.size() != expectedParameters.length) {
457 | String error = String.format("@%s methods can only have %s parameter(s). (%s.%s)",
458 | annotation.getSimpleName(), method.parameters().length,
459 | element.getEnclosingElement().getSimpleName(), element.getSimpleName());
460 | throw new ProcessingException(element, error);
461 | }
462 |
463 | for (int i = 0; i < parameters.size(); i++) {
464 | VariableElement parameter = parameters.get(i);
465 | TypeMirror methodParameterType = parameter.asType();
466 | String expectedType = expectedParameters[i];
467 | if (!expectedType.equals(methodParameterType.toString())) {
468 | String error = String.format(
469 | "Method parameters are not valid for @%s annotated method. Expected parameters of type(s): %s. (%s.%s)",
470 | annotation.getSimpleName(), Joiner.on(", ").join(expectedParameters),
471 | element.getEnclosingElement().getSimpleName(), element.getSimpleName());
472 | throw new ProcessingException(element, error);
473 | }
474 | }
475 | }
476 | }
477 |
--------------------------------------------------------------------------------
/sensorannotations-compiler/src/main/java/com/dvoiss/sensorannotations/SensorAnnotationsProcessor.java:
--------------------------------------------------------------------------------
1 | package com.dvoiss.sensorannotations;
2 |
3 | import com.dvoiss.sensorannotations.exception.ProcessingException;
4 | import java.io.IOException;
5 | import java.lang.annotation.Annotation;
6 | import java.util.HashSet;
7 | import java.util.LinkedHashMap;
8 | import java.util.Map;
9 | import java.util.Set;
10 | import javax.annotation.processing.AbstractProcessor;
11 | import javax.annotation.processing.Filer;
12 | import javax.annotation.processing.Messager;
13 | import javax.annotation.processing.ProcessingEnvironment;
14 | import javax.annotation.processing.RoundEnvironment;
15 | import javax.lang.model.SourceVersion;
16 | import javax.lang.model.element.Element;
17 | import javax.lang.model.element.ElementKind;
18 | import javax.lang.model.element.ExecutableElement;
19 | import javax.lang.model.element.Modifier;
20 | import javax.lang.model.element.TypeElement;
21 | import javax.lang.model.util.Elements;
22 | import javax.tools.Diagnostic;
23 | import org.checkerframework.checker.nullness.qual.NonNull;
24 | import org.checkerframework.checker.nullness.qual.Nullable;
25 |
26 | /**
27 | * The main annotation processor for the library. See {@link SensorAnnotationsFileBuilder} for more
28 | * info.
29 | */
30 | public class SensorAnnotationsProcessor extends AbstractProcessor {
31 | private static final boolean DEBUG_LOGGING = false;
32 |
33 | @NonNull private Elements mElementUtils;
34 | @NonNull private Filer mFiler;
35 | @NonNull private Messager mMessager;
36 |
37 | /**
38 | * Mapping between classes and a wrapper object containing all the annotated methods on it.
39 | */
40 | @NonNull private final Map mGroupedMethodsMap =
41 | new LinkedHashMap<>();
42 |
43 | @Override
44 | public synchronized void init(@NonNull ProcessingEnvironment env) {
45 | super.init(env);
46 | mMessager = processingEnv.getMessager();
47 | mFiler = processingEnv.getFiler();
48 | mElementUtils = processingEnv.getElementUtils();
49 | }
50 |
51 | @Override
52 | public boolean process(@NonNull Set extends TypeElement> annotations,
53 | @NonNull RoundEnvironment roundEnv) {
54 | try {
55 | processAnnotation(OnSensorChanged.class, roundEnv);
56 | processAnnotation(OnAccuracyChanged.class, roundEnv);
57 | processAnnotation(OnSensorNotAvailable.class, roundEnv);
58 | processAnnotation(OnTrigger.class, roundEnv);
59 | } catch (ProcessingException e) {
60 | error(e.getElement(), e.getMessage());
61 | }
62 |
63 | try {
64 | warn(null, "Preparing to create %d generated classes.", mGroupedMethodsMap.size());
65 |
66 | // If we've gotten here we've found all the annotations and grouped them accordingly.
67 | // Now generate the SensorBinder classes.
68 | SensorAnnotationsFileBuilder.generateCode(mGroupedMethodsMap, mElementUtils, mFiler);
69 |
70 | // Clear the map so a future processing round doesn't re-process the same annotations.
71 | mGroupedMethodsMap.clear();
72 | } catch (IOException e) {
73 | error(null, e.getMessage());
74 | } catch (ProcessingException e) {
75 | error(e.getElement(), e.getMessage());
76 | }
77 |
78 | return true;
79 | }
80 |
81 | private void processAnnotation(Class extends Annotation> annotationClass,
82 | @NonNull RoundEnvironment roundEnv) throws ProcessingException {
83 | Set extends Element> elements = roundEnv.getElementsAnnotatedWith(annotationClass);
84 |
85 | warn(null, "Processing %d elements annotated with @%s", elements.size(), elements);
86 |
87 | for (Element element : elements) {
88 | if (element.getKind() != ElementKind.METHOD) {
89 | throw new ProcessingException(element,
90 | String.format("Only methods can be annotated with @%s",
91 | annotationClass.getSimpleName()));
92 | } else {
93 | ExecutableElement executableElement = (ExecutableElement) element;
94 |
95 | try {
96 | processMethod(executableElement, annotationClass);
97 | } catch (IllegalArgumentException e) {
98 | throw new ProcessingException(executableElement, e.getMessage());
99 | }
100 | }
101 | }
102 | }
103 |
104 | private void processMethod(ExecutableElement executableElement,
105 | Class extends Annotation> annotationClass) throws ProcessingException {
106 | AnnotatedMethod annotatedMethod = new AnnotatedMethod(executableElement, annotationClass);
107 |
108 | checkMethodValidity(annotatedMethod);
109 |
110 | TypeElement enclosingClass = findEnclosingClass(annotatedMethod);
111 | if (enclosingClass == null) {
112 | throw new ProcessingException(null,
113 | String.format("Can not find enclosing class for method %s",
114 | annotatedMethod.getExecutableElement().getSimpleName().toString()));
115 | } else {
116 | String enclosingClassName = enclosingClass.getQualifiedName().toString();
117 | AnnotatedMethodsPerClass groupedMethods = mGroupedMethodsMap.get(enclosingClassName);
118 | if (groupedMethods == null) {
119 | groupedMethods = new AnnotatedMethodsPerClass(enclosingClassName);
120 | mGroupedMethodsMap.put(enclosingClassName, groupedMethods);
121 | }
122 |
123 | groupedMethods.add(annotationClass, annotatedMethod);
124 | }
125 | }
126 |
127 | private void checkMethodValidity(@NonNull AnnotatedMethod item) throws ProcessingException {
128 | ExecutableElement methodElement = item.getExecutableElement();
129 | Set modifiers = methodElement.getModifiers();
130 |
131 | // The annotated method needs to be accessible by the generated class which will have
132 | // the same package. Public or "package private" (default) methods are required.
133 | if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
134 | throw new ProcessingException(methodElement,
135 | String.format("The method %s can not be private or protected.",
136 | methodElement.getSimpleName().toString()));
137 | }
138 |
139 | // We cannot annotate abstract methods, we need to annotate the actual implementation of
140 | // the method on the implementing class.
141 | if (modifiers.contains(Modifier.ABSTRACT)) {
142 | throw new ProcessingException(methodElement, String.format(
143 | "The method %s is abstract. You can't annotate abstract methods with @%s",
144 | methodElement.getSimpleName().toString(), AnnotatedMethod.class.getSimpleName()));
145 | }
146 | }
147 |
148 | @Nullable
149 | private TypeElement findEnclosingClass(@NonNull AnnotatedMethod annotatedMethod) {
150 | TypeElement enclosingClass;
151 |
152 | ExecutableElement methodElement = annotatedMethod.getExecutableElement();
153 | while (true) {
154 | Element enclosingElement = methodElement.getEnclosingElement();
155 | if (enclosingElement.getKind() == ElementKind.CLASS) {
156 | enclosingClass = (TypeElement) enclosingElement;
157 | break;
158 | }
159 | }
160 |
161 | return enclosingClass;
162 | }
163 |
164 | @NonNull
165 | @Override
166 | public Set getSupportedAnnotationTypes() {
167 | Set types = new HashSet<>();
168 | types.add(OnSensorChanged.class.getCanonicalName());
169 | types.add(OnAccuracyChanged.class.getCanonicalName());
170 | types.add(OnSensorNotAvailable.class.getCanonicalName());
171 | types.add(OnTrigger.class.getCanonicalName());
172 | return types;
173 | }
174 |
175 | @NonNull
176 | @Override
177 | public SourceVersion getSupportedSourceVersion() {
178 | return SourceVersion.latestSupported();
179 | }
180 |
181 | private void error(@Nullable Element e, @NonNull String msg, @Nullable Object... args) {
182 | mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
183 | }
184 |
185 | private void warn(@Nullable Element e, @NonNull String msg, @Nullable Object... args) {
186 | if (DEBUG_LOGGING) {
187 | mMessager.printMessage(Diagnostic.Kind.WARNING, String.format(msg, args), e);
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/sensorannotations-compiler/src/main/java/com/dvoiss/sensorannotations/exception/ProcessingException.java:
--------------------------------------------------------------------------------
1 | package com.dvoiss.sensorannotations.exception;
2 |
3 | import javax.lang.model.element.Element;
4 | import org.checkerframework.checker.nullness.qual.Nullable;
5 |
6 | /**
7 | * Processor exception class for errors that occur during processing.
8 | */
9 | public class ProcessingException extends Exception {
10 | @Nullable private final Element mElement;
11 |
12 | public ProcessingException(@Nullable Element element, @Nullable String message) {
13 | super(message);
14 | this.mElement = element;
15 | }
16 |
17 | @Nullable
18 | public Element getElement() {
19 | return mElement;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sensorannotations-compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | com.dvoiss.sensorannotations.SensorAnnotationsProcessor
2 |
--------------------------------------------------------------------------------
/sensorannotations-lib/build.gradle:
--------------------------------------------------------------------------------
1 | import org.gradle.internal.jvm.Jvm
2 |
3 | apply plugin: 'com.android.library'
4 | apply plugin: 'com.novoda.bintray-release'
5 | apply from: '../config/quality/quality.gradle'
6 |
7 | android {
8 | compileSdkVersion rootProject.ext.compileSdkVersion
9 | buildToolsVersion rootProject.ext.buildToolsVersion
10 |
11 | defaultConfig {
12 | minSdkVersion rootProject.ext.minSdkVersion
13 | consumerProguardFiles 'proguard-rules.txt'
14 | testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility sourceCompatibilityVersion
19 | targetCompatibility targetCompatibilityVersion
20 | }
21 | }
22 |
23 | dependencies {
24 | compile project(':sensorannotations-annotations')
25 | compile deps.supportannotations
26 |
27 | testCompile project(':sensorannotations-compiler')
28 | testCompile deps.junit
29 | testCompile deps.truth
30 | testCompile deps.robolectric
31 | testCompile deps.compiletesting
32 | testCompile files(Jvm.current().getRuntimeJar())
33 | testCompile files(Jvm.current().getToolsJar())
34 | }
35 |
36 | publish {
37 | userOrg = 'dvoiss'
38 | groupId = 'com.dvoiss'
39 | artifactId = 'sensorannotations'
40 | publishVersion = rootProject.ext.version
41 | description = 'Annotate methods to use as listeners for a specific sensor.'
42 | website = 'https://github.com/dvoiss/sensorannotations'
43 | }
44 |
--------------------------------------------------------------------------------
/sensorannotations-lib/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | -keep public class * implements com.dvoiss.sensorannotations.internal.Sensorbinder { public (...); }
2 |
3 | -keep class com.dvoiss.sensorannotations.*
4 | -keepclasseswithmembernames class * { @com.dvoiss.sensorannotations.* ; }
5 |
--------------------------------------------------------------------------------
/sensorannotations-lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sensorannotations-lib/src/main/java/com/dvoiss/sensorannotations/SensorAnnotations.java:
--------------------------------------------------------------------------------
1 | package com.dvoiss.sensorannotations;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.util.Log;
7 | import com.dvoiss.sensorannotations.internal.SensorBinder;
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.util.LinkedHashMap;
10 | import java.util.Map;
11 |
12 | /**
13 | * Annotating methods as listeners for use with Android sensors.
14 | *
15 | * Binding needs a {@link Context} object to use to find the sensor manager. Binding can also be
16 | * done on a specific object with {@linkplain #bind(Object, Context)}.
17 | *
18 | * Per the best practices guidelines for sensors (Sensors
19 | * Best Practices), SensorAnnotations needs to be able to unregister listeners. This is best
20 | * done in an onPause method of the activity lifecycle.
21 | *
22 | * A good portion of this class is based on ButterKnife's binding code
23 | * (https://github.com/JakeWharton/butterknife).
24 | */
25 | public class SensorAnnotations {
26 | private static final boolean DEBUG_LOGGING = BuildConfig.DEBUG;
27 | private static final String TAG = SensorAnnotations.class.getSimpleName();
28 |
29 | private static final String ANDROID_PREFIX = "android.";
30 | private static final String JAVA_PREFIX = "java.";
31 |
32 | /**
33 | * The suffix will be added to the name of the generated class.
34 | */
35 | private static final String SUFFIX = "$$SensorBinder";
36 |
37 | static final SensorBinder NO_OP_VIEW_BINDER = new SensorBinder