7 package com.google.appinventor.components.runtime;
21 import android.os.Handler;
23 import java.util.HashMap;
34 @DesignerComponent(version = YaVersion.NXT_COLORSENSOR_COMPONENT_VERSION,
35 description =
"A component that provides a high-level interface to a color sensor on a " +
36 "LEGO MINDSTORMS NXT robot.",
37 category = ComponentCategory.LEGOMINDSTORMS,
39 iconName =
"images/legoMindstormsNxt.png")
43 private enum State { UNKNOWN, BELOW_RANGE, WITHIN_RANGE, ABOVE_RANGE }
44 private static final String DEFAULT_SENSOR_PORT =
"3";
45 private static final int DEFAULT_BOTTOM_OF_RANGE = 256;
46 private static final int DEFAULT_TOP_OF_RANGE = 767;
48 static final int SENSOR_TYPE_COLOR_FULL = 0x0D;
49 static final int SENSOR_TYPE_COLOR_RED = 0x0E;
50 static final int SENSOR_TYPE_COLOR_GREEN = 0x0F;
51 static final int SENSOR_TYPE_COLOR_BLUE = 0x10;
52 static final int SENSOR_TYPE_COLOR_NONE = 0x11;
57 mapColorToSensorType =
new HashMap<Integer, Integer>();
63 mapSensorValueToColor =
new HashMap<Integer, Integer>();
72 private boolean detectColor;
73 private Handler handler;
74 private final Runnable sensorReader;
77 private int previousColor;
78 private boolean colorChangedEventEnabled;
81 private State previousState;
82 private int bottomOfRange;
83 private int topOfRange;
84 private boolean belowRangeEventEnabled;
85 private boolean withinRangeEventEnabled;
86 private boolean aboveRangeEventEnabled;
87 private int generateColor;
93 super(container,
"NxtColorSensor");
94 handler =
new Handler();
95 previousState = State.UNKNOWN;
97 sensorReader =
new Runnable() {
102 SensorValue<Integer> sensorValue = getColorValue(
"");
103 if (sensorValue.valid) {
104 int currentColor = sensorValue.value;
106 if (currentColor != previousColor) {
110 previousColor = currentColor;
115 SensorValue<Integer> sensorValue = getLightValue(
"");
116 if (sensorValue.valid) {
118 if (sensorValue.value < bottomOfRange) {
119 currentState = State.BELOW_RANGE;
120 }
else if (sensorValue.value > topOfRange) {
121 currentState = State.ABOVE_RANGE;
123 currentState = State.WITHIN_RANGE;
126 if (currentState != previousState) {
127 if (currentState == State.BELOW_RANGE && belowRangeEventEnabled) {
130 if (currentState == State.WITHIN_RANGE && withinRangeEventEnabled) {
133 if (currentState == State.ABOVE_RANGE && aboveRangeEventEnabled) {
138 previousState = currentState;
142 if (isHandlerNeeded()) {
143 handler.post(sensorReader);
165 int sensorType = detectColor ? SENSOR_TYPE_COLOR_FULL : mapColorToSensorType.get(generateColor);
175 defaultValue = DEFAULT_SENSOR_PORT)
193 @
SimpleProperty(description =
"Whether the sensor should detect color or light. " +
194 "True indicates that the sensor should detect color; False indicates that the sensor " +
195 "should detect light. " +
196 "If the DetectColor property is set to True, the BelowRange, WithinRange, and AboveRange " +
197 "events will not occur and the sensor will not generate color. " +
198 "If the DetectColor property is set to False, the ColorChanged event will not occur.",
210 defaultValue =
"True")
213 boolean handlerWasNeeded = isHandlerNeeded();
215 this.detectColor = detectColor;
220 boolean handlerIsNeeded = isHandlerNeeded();
221 if (handlerWasNeeded && !handlerIsNeeded) {
222 handler.removeCallbacks(sensorReader);
225 previousState = State.UNKNOWN;
226 if (!handlerWasNeeded && handlerIsNeeded) {
227 handler.post(sensorReader);
233 @
SimpleFunction(description =
"Returns the current detected color, or the color None if the " +
234 "color can not be read or if the DetectColor property is set to False.")
236 String functionName =
"GetColor";
246 SensorValue<Integer> sensorValue = getColorValue(functionName);
247 if (sensorValue.valid) {
248 return sensorValue.value;
255 private SensorValue<Integer> getColorValue(String functionName) {
257 if (returnPackage !=
null) {
261 if (mapSensorValueToColor.containsKey(scaledValue)) {
262 int color = mapSensorValueToColor.get(scaledValue);
263 return new SensorValue<Integer>(
true, color);
269 return new SensorValue<Integer>(
false,
null);
276 @SimpleProperty(description =
"Whether the ColorChanged event should fire when the DetectColor" +
277 " property is set to True and the detected color changes.",
278 category = PropertyCategory.BEHAVIOR)
280 return colorChangedEventEnabled;
290 boolean handlerWasNeeded = isHandlerNeeded();
292 colorChangedEventEnabled = enabled;
294 boolean handlerIsNeeded = isHandlerNeeded();
295 if (handlerWasNeeded && !handlerIsNeeded) {
296 handler.removeCallbacks(sensorReader);
298 if (!handlerWasNeeded && handlerIsNeeded) {
300 handler.post(sensorReader);
304 @
SimpleEvent(description =
"Detected color has changed. " +
305 "The ColorChanged event will not occur if the DetectColor property is set to False or if " +
306 "the ColorChangedEventEnabled property is set to False.")
313 @
SimpleFunction(description =
"Returns the current light level as a value between 0 and 1023, " +
314 "or -1 if the light level can not be read or if the DetectColor property is set to True.")
316 String functionName =
"GetLightLevel";
326 SensorValue<Integer> sensorValue = getLightValue(functionName);
327 if (sensorValue.valid) {
328 return sensorValue.value;
335 private SensorValue<Integer> getLightValue(String functionName) {
337 if (returnPackage !=
null) {
341 return new SensorValue<Integer>(
true, normalizedValue);
346 return new SensorValue<Integer>(
false,
null);
353 @SimpleProperty(description =
"The bottom of the range used for the BelowRange, WithinRange," +
354 " and AboveRange events.",
355 category = PropertyCategory.BEHAVIOR)
357 return bottomOfRange;
365 defaultValue =
"" + DEFAULT_BOTTOM_OF_RANGE)
368 this.bottomOfRange = bottomOfRange;
369 previousState = State.UNKNOWN;
376 @
SimpleProperty(description =
"The top of the range used for the BelowRange, WithinRange, and" +
377 " AboveRange events.",
388 defaultValue =
"" + DEFAULT_TOP_OF_RANGE)
391 this.topOfRange = topOfRange;
392 previousState = State.UNKNOWN;
399 @
SimpleProperty(description =
"Whether the BelowRange event should fire when the DetectColor" +
400 " property is set to False and the light level goes below the BottomOfRange.",
403 return belowRangeEventEnabled;
413 boolean handlerWasNeeded = isHandlerNeeded();
415 belowRangeEventEnabled = enabled;
417 boolean handlerIsNeeded = isHandlerNeeded();
418 if (handlerWasNeeded && !handlerIsNeeded) {
419 handler.removeCallbacks(sensorReader);
421 if (!handlerWasNeeded && handlerIsNeeded) {
422 previousState = State.UNKNOWN;
423 handler.post(sensorReader);
427 @
SimpleEvent(description =
"Light level has gone below the range. " +
428 "The BelowRange event will not occur if the DetectColor property is set to True or if " +
429 "the BelowRangeEventEnabled property is set to False.")
439 @
SimpleProperty(description =
"Whether the WithinRange event should fire when the DetectColor" +
440 " property is set to False and the light level goes between the BottomOfRange and the " +
444 return withinRangeEventEnabled;
455 boolean handlerWasNeeded = isHandlerNeeded();
457 withinRangeEventEnabled = enabled;
459 boolean handlerIsNeeded = isHandlerNeeded();
460 if (handlerWasNeeded && !handlerIsNeeded) {
461 handler.removeCallbacks(sensorReader);
463 if (!handlerWasNeeded && handlerIsNeeded) {
464 previousState = State.UNKNOWN;
465 handler.post(sensorReader);
469 @
SimpleEvent(description =
"Light level has gone within the range. " +
470 "The WithinRange event will not occur if the DetectColor property is set to True or if " +
471 "the WithinRangeEventEnabled property is set to False.")
480 @
SimpleProperty(description =
"Whether the AboveRange event should fire when the DetectColor" +
481 " property is set to False and the light level goes above the TopOfRange.",
484 return aboveRangeEventEnabled;
494 boolean handlerWasNeeded = isHandlerNeeded();
496 aboveRangeEventEnabled = enabled;
498 boolean handlerIsNeeded = isHandlerNeeded();
499 if (handlerWasNeeded && !handlerIsNeeded) {
500 handler.removeCallbacks(sensorReader);
502 if (!handlerWasNeeded && handlerIsNeeded) {
503 previousState = State.UNKNOWN;
504 handler.post(sensorReader);
508 @
SimpleEvent(description =
"Light level has gone above the range. " +
509 "The AboveRange event will not occur if the DetectColor property is set to True or if " +
510 "the AboveRangeEventEnabled property is set to False.")
521 @
SimpleProperty(description =
"The color that should generated by the sensor. " +
522 "Only None, Red, Green, or Blue are valid values. " +
523 "The sensor will not generate color when the DetectColor property is set to True.",
526 return generateColor;
539 String functionName =
"GenerateColor";
540 if (mapColorToSensorType.containsKey(generateColor)) {
541 this.generateColor = generateColor;
551 private boolean isHandlerNeeded() {
553 return colorChangedEventEnabled;
555 return belowRangeEventEnabled || withinRangeEventEnabled || aboveRangeEventEnabled;
563 handler.removeCallbacks(sensorReader);