Unverified Commit 9ff06faf authored by dunandmuri's avatar dunandmuri Committed by GitHub

Implements visual accessibility features for designer and companion. (#2472)

parent e35a5b42
...@@ -8,6 +8,7 @@ package com.google.appinventor.client.editor.simple.components; ...@@ -8,6 +8,7 @@ package com.google.appinventor.client.editor.simple.components;
import static com.google.appinventor.client.Ode.MESSAGES; import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.editor.simple.SimpleEditor; import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.editor.youngandroid.YaFormEditor;
import com.google.appinventor.client.output.OdeLog; import com.google.appinventor.client.output.OdeLog;
import com.google.gwt.event.dom.client.ErrorEvent; import com.google.gwt.event.dom.client.ErrorEvent;
import com.google.gwt.event.dom.client.ErrorHandler; import com.google.gwt.event.dom.client.ErrorHandler;
...@@ -24,7 +25,7 @@ import com.google.gwt.user.client.ui.Image; ...@@ -24,7 +25,7 @@ import com.google.gwt.user.client.ui.Image;
* *
* @author lizlooney@google.com (Liz Looney) * @author lizlooney@google.com (Liz Looney)
*/ */
abstract class MockButtonBase extends MockVisibleComponent { abstract class MockButtonBase extends MockVisibleComponent implements FormChangeListener {
// Property names // Property names
private static final String PROPERTY_NAME_IMAGE = "Image"; private static final String PROPERTY_NAME_IMAGE = "Image";
...@@ -75,6 +76,19 @@ abstract class MockButtonBase extends MockVisibleComponent { ...@@ -75,6 +76,19 @@ abstract class MockButtonBase extends MockVisibleComponent {
deckPanel.add(image); deckPanel.add(image);
deckPanel.showWidget(0); deckPanel.showWidget(0);
initComponent(deckPanel); initComponent(deckPanel);
}
@Override
protected void onAttach() {
super.onAttach();
((YaFormEditor) editor).getForm().addFormChangeListener(this);
}
@Override
protected void onDetach() {
super.onDetach();
((YaFormEditor) editor).getForm().removeFormChangeListener(this);
} }
/** /**
...@@ -159,7 +173,12 @@ abstract class MockButtonBase extends MockVisibleComponent { ...@@ -159,7 +173,12 @@ abstract class MockButtonBase extends MockVisibleComponent {
return; return;
} }
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
MockComponentsUtil.resetWidgetBackgroundColor(buttonWidget); MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
MockComponentsUtil.setWidgetBackgroundColor(buttonWidget, "&HFF000000");
} else {
MockComponentsUtil.resetWidgetBackgroundColor(buttonWidget);
}
} else { } else {
MockComponentsUtil.setWidgetBackgroundColor(buttonWidget, text); MockComponentsUtil.setWidgetBackgroundColor(buttonWidget, text);
} }
...@@ -192,8 +211,17 @@ abstract class MockButtonBase extends MockVisibleComponent { ...@@ -192,8 +211,17 @@ abstract class MockButtonBase extends MockVisibleComponent {
* Sets the button's FontSize property to a new value. * Sets the button's FontSize property to a new value.
*/ */
private void setFontSizeProperty(String text) { private void setFontSizeProperty(String text) {
MockComponentsUtil.setWidgetFontSize(buttonWidget, text); float convertedText = Float.parseFloat(text);
updatePreferredSizeOfButton(); if (convertedText == 14.0 || convertedText == 24.0) {
MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("BigDefaultText").equals("True")) {
MockComponentsUtil.setWidgetFontSize(buttonWidget, "24");
} else {
MockComponentsUtil.setWidgetFontSize(buttonWidget, "14");
}
} else {
MockComponentsUtil.setWidgetFontSize(buttonWidget, text);
}
} }
/* /*
...@@ -242,7 +270,12 @@ abstract class MockButtonBase extends MockVisibleComponent { ...@@ -242,7 +270,12 @@ abstract class MockButtonBase extends MockVisibleComponent {
*/ */
private void setTextColorProperty(String text) { private void setTextColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
MockComponentsUtil.resetWidgetTextColor(buttonWidget); MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
MockComponentsUtil.setWidgetTextColor(buttonWidget, "&HFFFFFFFF");
} else {
MockComponentsUtil.resetWidgetTextColor(buttonWidget);
}
} else { } else {
MockComponentsUtil.setWidgetTextColor(buttonWidget, text); MockComponentsUtil.setWidgetTextColor(buttonWidget, text);
} }
...@@ -320,6 +353,43 @@ abstract class MockButtonBase extends MockVisibleComponent { ...@@ -320,6 +353,43 @@ abstract class MockButtonBase extends MockVisibleComponent {
setTextColorProperty(newValue); setTextColorProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_BUTTONSHAPE)){ } else if (propertyName.equals(PROPERTY_NAME_BUTTONSHAPE)){
setShapeProperty(newValue); setShapeProperty(newValue);
}
}
@Override
public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
if (component.getType().equals(MockForm.TYPE) && propertyName.equals("HighContrast")) {
setBackgroundColorProperty(getPropertyValue(PROPERTY_NAME_BACKGROUNDCOLOR));
setTextColorProperty(getPropertyValue(PROPERTY_NAME_TEXTCOLOR));
updatePreferredSizeOfButton();
refreshForm();
} }
else if (component.getType().equals(MockForm.TYPE) && propertyName.equals("BigDefaultText")) {
setFontSizeProperty(getPropertyValue(PROPERTY_NAME_FONTSIZE));
updatePreferredSizeOfButton();
refreshForm();
}
}
@Override
public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
}
@Override
public void onComponentAdded(MockComponent component) {
}
@Override
public void onComponentRenamed(MockComponent component, String oldName) {
}
@Override
public void onComponentSelectionChange(MockComponent component, boolean selected) {
} }
} }
...@@ -8,6 +8,7 @@ package com.google.appinventor.client.editor.simple.components; ...@@ -8,6 +8,7 @@ package com.google.appinventor.client.editor.simple.components;
import static com.google.appinventor.client.Ode.MESSAGES; import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.editor.simple.SimpleEditor; import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.editor.youngandroid.YaFormEditor;
import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer; import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
import com.google.gwt.user.client.ui.InlineHTML; import com.google.gwt.user.client.ui.InlineHTML;
...@@ -15,7 +16,7 @@ import com.google.gwt.user.client.ui.InlineHTML; ...@@ -15,7 +16,7 @@ import com.google.gwt.user.client.ui.InlineHTML;
* Mock Label component. * Mock Label component.
* *
*/ */
public final class MockLabel extends MockVisibleComponent { public final class MockLabel extends MockVisibleComponent implements FormChangeListener{
/** /**
* Component type name. * Component type name.
...@@ -41,8 +42,22 @@ public final class MockLabel extends MockVisibleComponent { ...@@ -41,8 +42,22 @@ public final class MockLabel extends MockVisibleComponent {
labelWidget = new InlineHTML(); labelWidget = new InlineHTML();
labelWidget.setStylePrimaryName("ode-SimpleMockComponent"); labelWidget.setStylePrimaryName("ode-SimpleMockComponent");
initComponent(labelWidget); initComponent(labelWidget);
} }
@Override
protected void onAttach() {
super.onAttach();
((YaFormEditor) editor).getForm().addFormChangeListener(this);
}
@Override
protected void onDetach() {
super.onDetach();
((YaFormEditor) editor).getForm().removeFormChangeListener(this);
}
@Override @Override
public void onCreateFromPalette() { public void onCreateFromPalette() {
// Change label text to component name // Change label text to component name
...@@ -84,7 +99,17 @@ public final class MockLabel extends MockVisibleComponent { ...@@ -84,7 +99,17 @@ public final class MockLabel extends MockVisibleComponent {
* Sets the label's FontSize property to a new value. * Sets the label's FontSize property to a new value.
*/ */
private void setFontSizeProperty(String text) { private void setFontSizeProperty(String text) {
MockComponentsUtil.setWidgetFontSize(labelWidget, text); float convertedText = Float.parseFloat(text);
if (convertedText == 14.0 || convertedText == 24.0) {
MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("BigDefaultText").equals("True")) {
MockComponentsUtil.setWidgetFontSize(labelWidget, "24");
} else {
MockComponentsUtil.setWidgetFontSize(labelWidget, "14");
}
} else {
MockComponentsUtil.setWidgetFontSize(labelWidget, text);
}
} }
/* /*
...@@ -157,4 +182,33 @@ public final class MockLabel extends MockVisibleComponent { ...@@ -157,4 +182,33 @@ public final class MockLabel extends MockVisibleComponent {
refreshForm(); refreshForm();
} }
} }
@Override
public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
if (component.getType().equals(MockForm.TYPE) && propertyName.equals("BigDefaultText")) {
setFontSizeProperty(getPropertyValue(PROPERTY_NAME_FONTSIZE));
refreshForm();
}
}
@Override
public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
}
@Override
public void onComponentAdded(MockComponent component) {
}
@Override
public void onComponentRenamed(MockComponent component, String oldName) {
}
@Override
public void onComponentSelectionChange(MockComponent component, boolean selected) {
}
} }
...@@ -11,13 +11,15 @@ import com.google.appinventor.components.common.ComponentConstants; ...@@ -11,13 +11,15 @@ import com.google.appinventor.components.common.ComponentConstants;
import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.PasswordTextBox; import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.google.appinventor.client.editor.youngandroid.YaFormEditor;
/** /**
* Mock PasswordTextBox component. * Mock PasswordTextBox component.
* *
* @author lizlooney@google.com (Liz Looney) * @author lizlooney@google.com (Liz Looney)
*/ */
public final class MockPasswordTextBox extends MockWrapper { public final class MockPasswordTextBox extends MockWrapper implements FormChangeListener{
/** /**
* Component type name. * Component type name.
...@@ -41,7 +43,16 @@ public final class MockPasswordTextBox extends MockWrapper { ...@@ -41,7 +43,16 @@ public final class MockPasswordTextBox extends MockWrapper {
passwordTextBoxWidget.setText("**********"); passwordTextBoxWidget.setText("**********");
initWrapper(passwordTextBoxWidget); initWrapper(passwordTextBoxWidget);
} }
@Override
protected void onAttach() {
super.onAttach();
((YaFormEditor) editor).getForm().addFormChangeListener(this);
}
@Override
protected void onDetach() {
super.onDetach();
((YaFormEditor) editor).getForm().removeFormChangeListener(this);
}
/** /**
* Class that extends PasswordTextBox so we can use a protected constructor. * Class that extends PasswordTextBox so we can use a protected constructor.
* *
...@@ -81,7 +92,12 @@ public final class MockPasswordTextBox extends MockWrapper { ...@@ -81,7 +92,12 @@ public final class MockPasswordTextBox extends MockWrapper {
*/ */
private void setBackgroundColorProperty(String text) { private void setBackgroundColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
text = "&HFFFFFFFF"; // white MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
text = "&HFF000000"; // black
} else {
text = "&HFFFFFFFF"; // white
}
} }
MockComponentsUtil.setWidgetBackgroundColor(passwordTextBoxWidget, text); MockComponentsUtil.setWidgetBackgroundColor(passwordTextBoxWidget, text);
} }
...@@ -137,7 +153,13 @@ public final class MockPasswordTextBox extends MockWrapper { ...@@ -137,7 +153,13 @@ public final class MockPasswordTextBox extends MockWrapper {
*/ */
private void setTextColorProperty(String text) { private void setTextColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
text = "&HFF000000"; // black MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
text = "&HFFFFFFFF"; // white
}
else {
text = "&HFF000000"; //black
}
} }
MockComponentsUtil.setWidgetTextColor(passwordTextBoxWidget, text); MockComponentsUtil.setWidgetTextColor(passwordTextBoxWidget, text);
} }
...@@ -173,4 +195,41 @@ public final class MockPasswordTextBox extends MockWrapper { ...@@ -173,4 +195,41 @@ public final class MockPasswordTextBox extends MockWrapper {
setTextColorProperty(newValue); setTextColorProperty(newValue);
} }
} }
@Override
public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
if (component.getType().equals(MockForm.TYPE) && propertyName.equals("HighContrast")) {
setBackgroundColorProperty(getPropertyValue(PROPERTY_NAME_BACKGROUNDCOLOR));
setTextColorProperty(getPropertyValue(PROPERTY_NAME_TEXTCOLOR));
if (propertyValue.equals("True")){
setFontSizeProperty("24");
refreshForm();
}
else {
setFontSizeProperty("14");
refreshForm();
}
}
}
@Override
public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
}
@Override
public void onComponentAdded(MockComponent component) {
}
@Override
public void onComponentRenamed(MockComponent component, String oldName) {
}
@Override
public void onComponentSelectionChange(MockComponent component, boolean selected) {
}
} }
...@@ -8,19 +8,21 @@ package com.google.appinventor.client.editor.simple.components; ...@@ -8,19 +8,21 @@ package com.google.appinventor.client.editor.simple.components;
import static com.google.appinventor.client.Ode.MESSAGES; import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.editor.simple.SimpleEditor; import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.editor.youngandroid.YaFormEditor;
import com.google.appinventor.components.common.ComponentConstants; import com.google.appinventor.components.common.ComponentConstants;
import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
/** /**
* Abstract superclass for textbox based mock components. * Abstract superclass for textbox based mock components.
* *
* @author sharon@google.com (Sharon Perl) * @author sharon@google.com (Sharon Perl)
* @author lizlooney@google.com (Liz Looney) * @author lizlooney@google.com (Liz Looney)
*/ */
abstract class MockTextBoxBase extends MockWrapper { abstract class MockTextBoxBase extends MockWrapper implements FormChangeListener{
// GWT widget used to mock a Simple TextBox // GWT widget used to mock a Simple TextBox
private final TextBox textBoxWidget; private final TextBox textBoxWidget;
...@@ -38,6 +40,16 @@ abstract class MockTextBoxBase extends MockWrapper { ...@@ -38,6 +40,16 @@ abstract class MockTextBoxBase extends MockWrapper {
initWrapper(textBoxWidget); initWrapper(textBoxWidget);
} }
@Override
protected void onAttach() {
super.onAttach();
((YaFormEditor) editor).getForm().addFormChangeListener(this);
}
@Override
protected void onDetach() {
super.onDetach();
((YaFormEditor) editor).getForm().removeFormChangeListener(this);
}
/** /**
* Class that extends TextBox so we can use a protected constructor. * Class that extends TextBox so we can use a protected constructor.
* *
...@@ -84,7 +96,12 @@ abstract class MockTextBoxBase extends MockWrapper { ...@@ -84,7 +96,12 @@ abstract class MockTextBoxBase extends MockWrapper {
*/ */
private void setBackgroundColorProperty(String text) { private void setBackgroundColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
text = "&HFFFFFFFF"; // white MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
text = "&HFF000000"; //black
} else {
text = "&HFFFFFFFF"; // white
}
} }
MockComponentsUtil.setWidgetBackgroundColor(textBoxWidget, text); MockComponentsUtil.setWidgetBackgroundColor(textBoxWidget, text);
} }
...@@ -116,7 +133,17 @@ abstract class MockTextBoxBase extends MockWrapper { ...@@ -116,7 +133,17 @@ abstract class MockTextBoxBase extends MockWrapper {
* Sets the textbox's FontSize property to a new value. * Sets the textbox's FontSize property to a new value.
*/ */
private void setFontSizeProperty(String text) { private void setFontSizeProperty(String text) {
MockComponentsUtil.setWidgetFontSize(textBoxWidget, text); float convertedText = Float.parseFloat(text);
if (convertedText == 14.0 || convertedText == 24.0) {
MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("BigDefaultText").equals("True")) {
MockComponentsUtil.setWidgetFontSize(textBoxWidget, "24");
} else {
MockComponentsUtil.setWidgetFontSize(textBoxWidget, "14");
}
} else {
MockComponentsUtil.setWidgetFontSize(textBoxWidget, text);
}
updatePreferredSize(); updatePreferredSize();
} }
...@@ -148,13 +175,23 @@ abstract class MockTextBoxBase extends MockWrapper { ...@@ -148,13 +175,23 @@ abstract class MockTextBoxBase extends MockWrapper {
*/ */
private void setTextColorProperty(String text) { private void setTextColorProperty(String text) {
if (MockComponentsUtil.isDefaultColor(text)) { if (MockComponentsUtil.isDefaultColor(text)) {
text = "&HFF000000"; // black MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("HighContrast").equals("True")) {
text = "&HFFFFFFFF"; // white
} else {
text = "&HFF000000"; //black
}
} }
MockComponentsUtil.setWidgetTextColor(textBoxWidget, text); MockComponentsUtil.setWidgetTextColor(textBoxWidget, text);
} }
// PropertyChangeListener implementation // PropertyChangeListener implementation
@Override @Override
public void onPropertyChange(String propertyName, String newValue) { public void onPropertyChange(String propertyName, String newValue) {
super.onPropertyChange(propertyName, newValue); super.onPropertyChange(propertyName, newValue);
...@@ -187,4 +224,40 @@ abstract class MockTextBoxBase extends MockWrapper { ...@@ -187,4 +224,40 @@ abstract class MockTextBoxBase extends MockWrapper {
setTextColorProperty(newValue); setTextColorProperty(newValue);
} }
} }
@Override
public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
if (component.getType().equals(MockForm.TYPE) && propertyName.equals("HighContrast")) {
setBackgroundColorProperty(getPropertyValue(PROPERTY_NAME_BACKGROUNDCOLOR));
setTextColorProperty(getPropertyValue(PROPERTY_NAME_TEXTCOLOR));
//setFontSizeProperty(getPropertyValue(PROPERTY_NAME_FONTSIZE));
updatePreferredSize();
refreshForm();
}
else if (component.getType().equals(MockForm.TYPE) && propertyName.equals("BigDefaultText")) {
setFontSizeProperty(getPropertyValue(PROPERTY_NAME_FONTSIZE));
updatePreferredSize();
refreshForm();
}
}
@Override
public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
}
@Override
public void onComponentAdded(MockComponent component) {
}
@Override
public void onComponentRenamed(MockComponent component, String oldName) {
}
@Override
public void onComponentSelectionChange(MockComponent component, boolean selected) {
}
} }
...@@ -6,10 +6,13 @@ ...@@ -6,10 +6,13 @@
package com.google.appinventor.client.editor.simple.components; package com.google.appinventor.client.editor.simple.components;
import com.google.appinventor.client.editor.simple.SimpleEditor; import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.editor.youngandroid.YaFormEditor;
import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import java.text.Normalizer;
import static com.google.appinventor.client.Ode.MESSAGES; import static com.google.appinventor.client.Ode.MESSAGES;
/** /**
...@@ -17,7 +20,7 @@ import static com.google.appinventor.client.Ode.MESSAGES; ...@@ -17,7 +20,7 @@ import static com.google.appinventor.client.Ode.MESSAGES;
* *
* @author srlane@mit.edu (Susan Rati Lane) * @author srlane@mit.edu (Susan Rati Lane)
*/ */
abstract class MockToggleBase<T extends Widget> extends MockWrapper { abstract class MockToggleBase<T extends Widget> extends MockWrapper implements FormChangeListener {
// Set toggle widget in child classes // Set toggle widget in child classes
protected T toggleWidget; protected T toggleWidget;
...@@ -85,8 +88,19 @@ abstract class MockToggleBase<T extends Widget> extends MockWrapper { ...@@ -85,8 +88,19 @@ abstract class MockToggleBase<T extends Widget> extends MockWrapper {
* Sets the toggle's FontSize property to a new value. * Sets the toggle's FontSize property to a new value.
*/ */
protected void setFontSizeProperty(String text) { protected void setFontSizeProperty(String text) {
MockComponentsUtil.setWidgetFontSize(toggleWidget, text); float convertedText = Float.parseFloat(text);
if (convertedText == 14.0 || convertedText == 24.0) {
MockForm form = ((YaFormEditor) editor).getForm();
if (form != null && form.getPropertyValue("BigDefaultText").equals("True")) {
MockComponentsUtil.setWidgetFontSize(toggleWidget, "24");
} else {
MockComponentsUtil.setWidgetFontSize(toggleWidget, "14");
}
} else {
MockComponentsUtil.setWidgetFontSize(toggleWidget, text);
}
updatePreferredSize(); updatePreferredSize();
} }
/* /*
...@@ -145,4 +159,32 @@ abstract class MockToggleBase<T extends Widget> extends MockWrapper { ...@@ -145,4 +159,32 @@ abstract class MockToggleBase<T extends Widget> extends MockWrapper {
preferredSize = MockComponentsUtil preferredSize = MockComponentsUtil
.getPreferredSizeOfElement(DOM.clone(toggleWidget.getElement(), true)); .getPreferredSizeOfElement(DOM.clone(toggleWidget.getElement(), true));
} }
@Override
public void onComponentPropertyChanged(MockComponent component, String propertyName, String propertyValue) {
if (component.getType().equals(MockForm.TYPE) && propertyName.equals("BigDefaultText")) {
setFontSizeProperty(getPropertyValue(PROPERTY_NAME_FONTSIZE));
refreshForm();
}
}
@Override
public void onComponentRemoved(MockComponent component, boolean permanentlyDeleted) {
}
@Override
public void onComponentAdded(MockComponent component) {
}
@Override
public void onComponentRenamed(MockComponent component, String oldName) {
}
@Override
public void onComponentSelectionChange(MockComponent component, boolean selected) {
}
} }
...@@ -1051,6 +1051,11 @@ public final class YoungAndroidFormUpgrader { ...@@ -1051,6 +1051,11 @@ public final class YoungAndroidFormUpgrader {
srcCompVersion = 27; srcCompVersion = 27;
} }
if (srcCompVersion < 28) {
// HighContrast and BigDefaultText properties were added.
srcCompVersion = 28;
}
return srcCompVersion; return srcCompVersion;
} }
...@@ -1114,6 +1119,10 @@ public final class YoungAndroidFormUpgrader { ...@@ -1114,6 +1119,10 @@ public final class YoungAndroidFormUpgrader {
// The Clickable property was added. // The Clickable property was added.
srcCompVersion = 4; srcCompVersion = 4;
} }
if (srcCompVersion < 5) {
// The AlternateText property was added.
srcCompVersion = 5;
}
return srcCompVersion; return srcCompVersion;
} }
......
...@@ -1606,7 +1606,10 @@ Blockly.Versioning.AllUpgradeMaps = ...@@ -1606,7 +1606,10 @@ Blockly.Versioning.AllUpgradeMaps =
// Click event was added // Click event was added
// The Clickable property was added. // The Clickable property was added.
4: "noUpgrade" 4: "noUpgrade",
// AlternateText property was added.
5: "noUpgrade"
}, // End Image upgraders }, // End Image upgraders
...@@ -2366,7 +2369,11 @@ Blockly.Versioning.AllUpgradeMaps = ...@@ -2366,7 +2369,11 @@ Blockly.Versioning.AllUpgradeMaps =
// For FORM_COMPONENT_VERSION 27: // For FORM_COMPONENT_VERSION 27:
// - Platform and PlatformVersion read-only blocks were added // - Platform and PlatformVersion read-only blocks were added
27: "noUpgrade" 27: "noUpgrade",
// For FORM_COMPONENT_VERSION 28:
// - HighContrast and BigDefaultText properties were added
28: "noUpgrade"
}, // End Screen }, // End Screen
......
...@@ -898,7 +898,10 @@ public class YaVersion { ...@@ -898,7 +898,10 @@ public class YaVersion {
// - Updated the default value of ShowListsAsJson from false -> true // - Updated the default value of ShowListsAsJson from false -> true
// For FORM_COMPONENT_VERSION 27: // For FORM_COMPONENT_VERSION 27:
// - Added the Platform and PlatformVersion read-only blocks // - Added the Platform and PlatformVersion read-only blocks
public static final int FORM_COMPONENT_VERSION = 27; // For FORM_COMPONENT_VERSION 28:
// - Added the AlternateText property
// - Added the BigDefaultText property
public static final int FORM_COMPONENT_VERSION = 28;
// For FUSIONTABLESCONTROL_COMPONENT_VERSION 2: // For FUSIONTABLESCONTROL_COMPONENT_VERSION 2:
// - The Fusiontables API was migrated from SQL to V1 // - The Fusiontables API was migrated from SQL to V1
...@@ -931,7 +934,9 @@ public class YaVersion { ...@@ -931,7 +934,9 @@ public class YaVersion {
// For IMAGE_COMPONENT_VERSION 4: // For IMAGE_COMPONENT_VERSION 4:
// - The Click event was added. // - The Click event was added.
// - The Clickable property was added. // - The Clickable property was added.
public static final int IMAGE_COMPONENT_VERSION = 4; // For IMAGE_COMPONENT_VERSION 5:
// - The AlternateText property was added.
public static final int IMAGE_COMPONENT_VERSION = 5;
// For IMAGEPICKER_COMPONENT_VERSION 2: // For IMAGEPICKER_COMPONENT_VERSION 2:
// - The Alignment property was renamed to TextAlignment. // - The Alignment property was renamed to TextAlignment.
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2020-2021 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.runtime;
public interface AccessibleComponent {
//Sets the high contrast field of a component to be either True or False
void setHighContrast(boolean isHighContrast);
//Returns whether a component is in high contrast mode or not
boolean getHighContrast();
//Sets the large font field of a component to be either True or False
void setLargeFont(boolean isLargeFont);
//Returns whether a component is in large font mode or not
boolean getLargeFont();
}
...@@ -45,7 +45,7 @@ import java.io.IOException; ...@@ -45,7 +45,7 @@ import java.io.IOException;
@SimpleObject @SimpleObject
@UsesPermissions(permissionNames = "android.permission.INTERNET") @UsesPermissions(permissionNames = "android.permission.INTERNET")
public abstract class ButtonBase extends AndroidViewComponent public abstract class ButtonBase extends AndroidViewComponent
implements OnClickListener, OnFocusChangeListener, OnLongClickListener, View.OnTouchListener { implements OnClickListener, OnFocusChangeListener, OnLongClickListener, View.OnTouchListener, AccessibleComponent {
private static final String LOG_TAG = "ButtonBase"; private static final String LOG_TAG = "ButtonBase";
...@@ -103,6 +103,12 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -103,6 +103,12 @@ public abstract class ButtonBase extends AndroidViewComponent
// could not be loaded, this is null. // could not be loaded, this is null.
private Drawable backgroundImageDrawable; private Drawable backgroundImageDrawable;
//Whether or not the button is in high contrast mode
private boolean isHighContrast = false;
//Whether or not the button is in big text mode
private boolean isBigText = false;
/** /**
* The minimum width of a button for the current theme. * The minimum width of a button for the current theme.
* *
...@@ -158,6 +164,9 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -158,6 +164,9 @@ public abstract class ButtonBase extends AndroidViewComponent
Shape(Component.BUTTON_SHAPE_DEFAULT); Shape(Component.BUTTON_SHAPE_DEFAULT);
} }
public void Initialize(){
updateAppearance();
}
/** /**
* If a custom background images is specified for the button, then it will lose the pressed * If a custom background images is specified for the button, then it will lose the pressed
* and disabled image effects; no visual feedback. * and disabled image effects; no visual feedback.
...@@ -343,7 +352,6 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -343,7 +352,6 @@ public abstract class ButtonBase extends AndroidViewComponent
// Clear the prior background image. // Clear the prior background image.
backgroundImageDrawable = null; backgroundImageDrawable = null;
// Load image from file. // Load image from file.
if (imagePath.length() > 0) { if (imagePath.length() > 0) {
try { try {
...@@ -402,7 +410,15 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -402,7 +410,15 @@ public abstract class ButtonBase extends AndroidViewComponent
if (backgroundColor == Component.COLOR_DEFAULT) { if (backgroundColor == Component.COLOR_DEFAULT) {
// If there is no background image and color is default, // If there is no background image and color is default,
// restore original 3D bevel appearance. // restore original 3D bevel appearance.
ViewUtil.setBackgroundDrawable(view, defaultButtonDrawable); if (isHighContrast || container.$form().HighContrast()) {
ViewUtil.setBackgroundDrawable(view, null);
ViewUtil.setBackgroundDrawable(view, getSafeBackgroundDrawable());
view.getBackground().setColorFilter(Component.COLOR_BLACK, PorterDuff.Mode.SRC_ATOP);
}
else {
ViewUtil.setBackgroundDrawable(view, defaultButtonDrawable);
}
} else if (backgroundColor == Component.COLOR_NONE) { } else if (backgroundColor == Component.COLOR_NONE) {
// Clear the background image. // Clear the background image.
ViewUtil.setBackgroundDrawable(view, null); ViewUtil.setBackgroundDrawable(view, null);
...@@ -490,7 +506,11 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -490,7 +506,11 @@ public abstract class ButtonBase extends AndroidViewComponent
view.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.CLEAR); view.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.CLEAR);
} }
else if (backgroundColor == Component.COLOR_DEFAULT) { else if (backgroundColor == Component.COLOR_DEFAULT) {
view.getBackground().setColorFilter(SHAPED_DEFAULT_BACKGROUND_COLOR, PorterDuff.Mode.SRC_ATOP); if (isHighContrast || container.$form().HighContrast()) {
view.getBackground().setColorFilter(Component.COLOR_BLACK, PorterDuff.Mode.SRC_ATOP);
} else {
view.getBackground().setColorFilter(SHAPED_DEFAULT_BACKGROUND_COLOR, PorterDuff.Mode.SRC_ATOP);
}
} }
else { else {
view.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.SRC_ATOP); view.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.SRC_ATOP);
...@@ -638,7 +658,17 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -638,7 +658,17 @@ public abstract class ButtonBase extends AndroidViewComponent
@SimpleProperty( @SimpleProperty(
category = PropertyCategory.APPEARANCE) category = PropertyCategory.APPEARANCE)
public void FontSize(float size) { public void FontSize(float size) {
TextViewUtil.setFontSize(view, size); if (Math.abs(size-Component.FONT_DEFAULT_SIZE)<.01 || Math.abs(size-24)<.01) {
if (container.$form().BigDefaultText()) {
TextViewUtil.setFontSize(view, 24);
}
else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
else {
TextViewUtil.setFontSize(view, size);
}
} }
/** /**
...@@ -729,7 +759,12 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -729,7 +759,12 @@ public abstract class ButtonBase extends AndroidViewComponent
if (argb != Component.COLOR_DEFAULT) { if (argb != Component.COLOR_DEFAULT) {
TextViewUtil.setTextColor(view, argb); TextViewUtil.setTextColor(view, argb);
} else { } else {
TextViewUtil.setTextColors(view, defaultColorStateList); if (isHighContrast || container.$form().HighContrast()){
TextViewUtil.setTextColor(view, Color.WHITE);
}
else {
TextViewUtil.setTextColors(view, defaultColorStateList);
}
} }
} }
...@@ -768,4 +803,48 @@ public abstract class ButtonBase extends AndroidViewComponent ...@@ -768,4 +803,48 @@ public abstract class ButtonBase extends AndroidViewComponent
return longClick(); return longClick();
} }
@Override
public void setHighContrast(boolean isHighContrast) {
//background of button
if (backgroundImageDrawable == null && shape == Component.BUTTON_SHAPE_DEFAULT && backgroundColor == Component.COLOR_DEFAULT) {
if (isHighContrast) {
ViewUtil.setBackgroundDrawable(view, null);
ViewUtil.setBackgroundDrawable(view, getSafeBackgroundDrawable());
view.getBackground().setColorFilter(Component.COLOR_BLACK, PorterDuff.Mode.SRC_ATOP);
} else {
ViewUtil.setBackgroundDrawable(view, defaultButtonDrawable);
}
}
//color of text
if (textColor == Component.COLOR_DEFAULT) {
if (isHighContrast) {
TextViewUtil.setTextColor(view, Color.WHITE);
} else {
TextViewUtil.setTextColors(view, defaultColorStateList);
}
}
}
@Override
public boolean getHighContrast() {
return isHighContrast;
}
@Override
public void setLargeFont(boolean isLargeFont) {
if (TextViewUtil.getFontSize(view, container.$context()) == 24.0 || TextViewUtil.getFontSize(view, container.$context()) == Component.FONT_DEFAULT_SIZE) {
if (isLargeFont) {
TextViewUtil.setFontSize(view, 24);
} else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
}
@Override
public boolean getLargeFont() {
return isBigText;
}
} }
...@@ -886,6 +886,11 @@ public final class Canvas extends AndroidViewComponent implements ComponentConta ...@@ -886,6 +886,11 @@ public final class Canvas extends AndroidViewComponent implements ComponentConta
throw new UnsupportedOperationException("Canvas.$add() called"); throw new UnsupportedOperationException("Canvas.$add() called");
} }
@Override
public List<? extends Component> getChildren(){
return sprites;
}
@Override @Override
public void setChildWidth(AndroidViewComponent component, int width) { public void setChildWidth(AndroidViewComponent component, int width) {
throw new UnsupportedOperationException("Canvas.setChildWidth() called"); throw new UnsupportedOperationException("Canvas.setChildWidth() called");
......
...@@ -8,6 +8,8 @@ package com.google.appinventor.components.runtime; ...@@ -8,6 +8,8 @@ package com.google.appinventor.components.runtime;
import android.app.Activity; import android.app.Activity;
import java.util.List;
/** /**
* Components that can contain other components need to implement this * Components that can contain other components need to implement this
* interface. * interface.
...@@ -44,6 +46,8 @@ public interface ComponentContainer { ...@@ -44,6 +46,8 @@ public interface ComponentContainer {
void setChildHeight(AndroidViewComponent component, int height); void setChildHeight(AndroidViewComponent component, int height);
List<? extends Component> getChildren();
int Width(); int Width();
int Height(); int Height();
......
...@@ -142,6 +142,8 @@ public class Form extends AppInventorCompatActivity ...@@ -142,6 +142,8 @@ public class Form extends AppInventorCompatActivity
private static final int DEFAULT_ACCENT_COLOR = private static final int DEFAULT_ACCENT_COLOR =
hexStringToInt(ComponentConstants.DEFAULT_ACCENT_COLOR); hexStringToInt(ComponentConstants.DEFAULT_ACCENT_COLOR);
private List<Component> allChildren = new ArrayList<>();
// Keep track of the current form object. // Keep track of the current form object.
// activeForm always holds the Form that is currently handling event dispatching so runtime.scm // activeForm always holds the Form that is currently handling event dispatching so runtime.scm
// can lookup symbols in the correct environment. // can lookup symbols in the correct environment.
...@@ -203,6 +205,9 @@ public class Form extends AppInventorCompatActivity ...@@ -203,6 +205,9 @@ public class Form extends AppInventorCompatActivity
private FrameLayout frameLayout; private FrameLayout frameLayout;
private boolean scrollable; private boolean scrollable;
private boolean highContrast;
private boolean bigDefaultText;
private ScaledFrameLayout scaleLayout; private ScaledFrameLayout scaleLayout;
private static boolean sCompatibilityMode; private static boolean sCompatibilityMode;
...@@ -261,6 +266,8 @@ public class Form extends AppInventorCompatActivity ...@@ -261,6 +266,8 @@ public class Form extends AppInventorCompatActivity
// FragmentActivity is added in future. // FragmentActivity is added in future.
public static final int MAX_PERMISSION_NONCE = 100000; public static final int MAX_PERMISSION_NONCE = 100000;
public static class PercentStorageRecord { public static class PercentStorageRecord {
public enum Dim { public enum Dim {
HEIGHT, WIDTH }; HEIGHT, WIDTH };
...@@ -446,6 +453,8 @@ public class Form extends AppInventorCompatActivity ...@@ -446,6 +453,8 @@ public class Form extends AppInventorCompatActivity
ActionBar(themeHelper.hasActionBar()); ActionBar(themeHelper.hasActionBar());
} }
Scrollable(false); // frameLayout is created in Scrollable() Scrollable(false); // frameLayout is created in Scrollable()
HighContrast(false);
BigDefaultText(false);
Sizing("Responsive"); // Note: Only the Screen1 value is used as this is per-project Sizing("Responsive"); // Note: Only the Screen1 value is used as this is per-project
AboutScreen(""); AboutScreen("");
BackgroundImage(""); BackgroundImage("");
...@@ -1118,6 +1127,83 @@ public class Form extends AppInventorCompatActivity ...@@ -1118,6 +1127,83 @@ public class Form extends AppInventorCompatActivity
}); });
} }
/**
* HighContrast property getter method.
*
* @return true if we want high constrast mode
*/
@SimpleProperty(category = PropertyCategory.APPEARANCE,
description = "When checked, we will use high contrast mode")
public boolean HighContrast() {
return highContrast;
}
/**
* When checked, there will be high contrast mode turned on.
*
* @param highContrast true if the high contrast mode is on
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN,
defaultValue = "False")
@SimpleProperty
public void HighContrast(boolean highContrast) {
//this.scrollable = scrollable;
this.highContrast=highContrast;
setHighContrastRecursive(this, highContrast);
recomputeLayout();
}
private static void setHighContrastRecursive(ComponentContainer container, boolean enabled) {
for (Component child : container.getChildren()) {
if (child instanceof ComponentContainer) {
setHighContrastRecursive((ComponentContainer) child, enabled);
} else if (child instanceof AccessibleComponent) {
((AccessibleComponent) child).setHighContrast(enabled);
}
}
}
/**
* BigDefaultText property getter method.
*
* @return true if we are in the big text mode
*/
@SimpleProperty(category = PropertyCategory.APPEARANCE,
description = "When checked, we will use high contrast mode")
public boolean BigDefaultText() {
return bigDefaultText;
}
/**
* When checked, all default size text will be increased in size.
*
* @param bigDefaultText true if the big text mode is on
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN,
defaultValue = "False")
@SimpleProperty
public void BigDefaultText(boolean bigDefaultText) {
//this.scrollable = scrollable;
this.bigDefaultText=bigDefaultText;
setBigDefaultTextRecursive(this, bigDefaultText);
recomputeLayout();
}
private static void setBigDefaultTextRecursive(ComponentContainer container, boolean enabled) {
for (Component child : container.getChildren()) {
if (child instanceof ComponentContainer) {
setBigDefaultTextRecursive((ComponentContainer) child, enabled);
} else if (child instanceof AccessibleComponent) {
((AccessibleComponent) child).setLargeFont(enabled);
}
}
}
/** /**
* Scrollable property getter method. * Scrollable property getter method.
* *
...@@ -2113,9 +2199,18 @@ public class Form extends AppInventorCompatActivity ...@@ -2113,9 +2199,18 @@ public class Form extends AppInventorCompatActivity
@Override @Override
public void $add(AndroidViewComponent component) { public void $add(AndroidViewComponent component) {
viewLayout.add(component); viewLayout.add(component);
allChildren.add(component);
}
@Override
public List<? extends Component> getChildren(){
return allChildren;
} }
public float deviceDensity(){ public float deviceDensity(){
return this.deviceDensity; return this.deviceDensity;
} }
......
...@@ -35,6 +35,8 @@ import com.google.appinventor.components.runtime.util.MediaUtil; ...@@ -35,6 +35,8 @@ import com.google.appinventor.components.runtime.util.MediaUtil;
import com.google.appinventor.components.runtime.util.ViewUtil; import com.google.appinventor.components.runtime.util.ViewUtil;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** /**
* A container for components that arranges them linearly, either * A container for components that arranges them linearly, either
...@@ -67,6 +69,9 @@ public class HVArrangement extends AndroidViewComponent implements Component, Co ...@@ -67,6 +69,9 @@ public class HVArrangement extends AndroidViewComponent implements Component, Co
// Image path // Image path
private String imagePath = ""; private String imagePath = "";
//list of component children
private List<Component> allChildren = new ArrayList<>();
private Drawable defaultButtonDrawable; private Drawable defaultButtonDrawable;
private final Handler androidUIHandler = new Handler(); private final Handler androidUIHandler = new Handler();
...@@ -144,6 +149,12 @@ public class HVArrangement extends AndroidViewComponent implements Component, Co ...@@ -144,6 +149,12 @@ public class HVArrangement extends AndroidViewComponent implements Component, Co
@Override @Override
public void $add(AndroidViewComponent component) { public void $add(AndroidViewComponent component) {
viewLayout.add(component); viewLayout.add(component);
allChildren.add(component);
}
@Override
public List<? extends Component> getChildren() {
return allChildren;
} }
@Override @Override
......
...@@ -88,6 +88,12 @@ public final class Image extends AndroidViewComponent { ...@@ -88,6 +88,12 @@ public final class Image extends AndroidViewComponent {
return view; return view;
} }
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "")
@SimpleProperty(description = "A written description of what the image looks like.")
public void AlternateText(String description){
view.setContentDescription(description);
}
@SimpleEvent(description = "An event that occurs when an image is clicked.") @SimpleEvent(description = "An event that occurs when an image is clicked.")
public void Click() { public void Click() {
EventDispatcher.dispatchEvent(this, "Click"); EventDispatcher.dispatchEvent(this, "Click");
......
...@@ -37,7 +37,7 @@ import android.widget.TextView; ...@@ -37,7 +37,7 @@ import android.widget.TextView;
"the appearance and placement of the text.", "the appearance and placement of the text.",
category = ComponentCategory.USERINTERFACE) category = ComponentCategory.USERINTERFACE)
@SimpleObject @SimpleObject
public final class Label extends AndroidViewComponent { public final class Label extends AndroidViewComponent implements AccessibleComponent{
// default margin around a label in DPs // default margin around a label in DPs
// note that the spacing between adjacent labels will be twice this value // note that the spacing between adjacent labels will be twice this value
...@@ -79,6 +79,9 @@ public final class Label extends AndroidViewComponent { ...@@ -79,6 +79,9 @@ public final class Label extends AndroidViewComponent {
// HTML content of the label // HTML content of the label
private String htmlContent; private String htmlContent;
//Whether or not the text should be big
private boolean isBigText = false;
/** /**
* Creates a new Label component. * Creates a new Label component.
* *
...@@ -312,7 +315,18 @@ private void setLabelMargins(boolean hasMargins) { ...@@ -312,7 +315,18 @@ private void setLabelMargins(boolean hasMargins) {
defaultValue = Component.FONT_DEFAULT_SIZE + "") defaultValue = Component.FONT_DEFAULT_SIZE + "")
@SimpleProperty @SimpleProperty
public void FontSize(float size) { public void FontSize(float size) {
TextViewUtil.setFontSize(view, size);
if (Math.abs(size-Component.FONT_DEFAULT_SIZE)<.01 || Math.abs(size-24)<.01) {
if (isBigText || container.$form().BigDefaultText()) {
TextViewUtil.setFontSize(view, 24);
}
else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
else {
TextViewUtil.setFontSize(view, size);
}
} }
/** /**
...@@ -457,4 +471,30 @@ private void setLabelMargins(boolean hasMargins) { ...@@ -457,4 +471,30 @@ private void setLabelMargins(boolean hasMargins) {
TextViewUtil.setTextColor(view, container.$form().isDarkTheme() ? Component.COLOR_WHITE : Component.COLOR_BLACK); TextViewUtil.setTextColor(view, container.$form().isDarkTheme() ? Component.COLOR_WHITE : Component.COLOR_BLACK);
} }
} }
@Override
public void setHighContrast(boolean isHighContrast) {
}
@Override
public boolean getHighContrast() {
return false;
}
@Override
public void setLargeFont(boolean isLargeFont) {
if (TextViewUtil.getFontSize(view, container.$context()) == 24.0 || TextViewUtil.getFontSize(view, container.$context()) == Component.FONT_DEFAULT_SIZE) {
if (isLargeFont) {
TextViewUtil.setFontSize(view, 24);
} else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
}
@Override
public boolean getLargeFont() {
return isBigText;
}
} }
...@@ -328,6 +328,11 @@ public abstract class MapFeatureContainerBase extends AndroidViewComponent imple ...@@ -328,6 +328,11 @@ public abstract class MapFeatureContainerBase extends AndroidViewComponent imple
throw new UnsupportedOperationException("Map.$add() called"); throw new UnsupportedOperationException("Map.$add() called");
} }
@Override
public List<? extends Component> getChildren(){
return features;
}
@Override @Override
public void setChildWidth(AndroidViewComponent component, int width) { public void setChildWidth(AndroidViewComponent component, int width) {
throw new UnsupportedOperationException("Map.setChildWidth called"); throw new UnsupportedOperationException("Map.setChildWidth called");
......
...@@ -18,6 +18,9 @@ import com.google.appinventor.components.runtime.util.ViewUtil; ...@@ -18,6 +18,9 @@ import com.google.appinventor.components.runtime.util.ViewUtil;
import android.app.Activity; import android.app.Activity;
import android.view.View; import android.view.View;
import java.util.ArrayList;
import java.util.List;
/** /**
* Use a table arrangement component to display a group of components in a tabular fashion. * Use a table arrangement component to display a group of components in a tabular fashion.
* *
...@@ -54,6 +57,8 @@ public class TableArrangement extends AndroidViewComponent ...@@ -54,6 +57,8 @@ public class TableArrangement extends AndroidViewComponent
// Layout // Layout
private final TableLayout viewLayout; private final TableLayout viewLayout;
private List<Component> allChildren;
/** /**
* Creates a new TableArrangement component. * Creates a new TableArrangement component.
* *
...@@ -127,6 +132,12 @@ public class TableArrangement extends AndroidViewComponent ...@@ -127,6 +132,12 @@ public class TableArrangement extends AndroidViewComponent
@Override @Override
public void $add(AndroidViewComponent component) { public void $add(AndroidViewComponent component) {
viewLayout.add(component); viewLayout.add(component);
allChildren.add(component);
}
@Override
public List<? extends Component> getChildren(){
return allChildren = new ArrayList<>();
} }
@Override @Override
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
package com.google.appinventor.components.runtime; package com.google.appinventor.components.runtime;
import android.graphics.Color;
import android.graphics.PorterDuff;
import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.IsColor; import com.google.appinventor.components.annotations.IsColor;
import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.PropertyCategory;
...@@ -35,7 +37,7 @@ import android.widget.EditText; ...@@ -35,7 +37,7 @@ import android.widget.EditText;
@SimpleObject @SimpleObject
public abstract class TextBoxBase extends AndroidViewComponent public abstract class TextBoxBase extends AndroidViewComponent
implements OnFocusChangeListener { implements OnFocusChangeListener, AccessibleComponent {
protected final EditText view; protected final EditText view;
...@@ -63,6 +65,15 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -63,6 +65,15 @@ public abstract class TextBoxBase extends AndroidViewComponent
// This is our handle on Android's nice 3-d default textbox. // This is our handle on Android's nice 3-d default textbox.
private Drawable defaultTextBoxDrawable; private Drawable defaultTextBoxDrawable;
//Whether or not the button is in high contrast mode
private boolean isHighContrast = false;
//Whether or not the button is in big text mode
private boolean isBigText = false;
//The default text color of the textbox hint, according to theme
private int hintColorDefault;
/** /**
* Creates a new TextBoxBase component * Creates a new TextBoxBase component
* *
...@@ -72,6 +83,7 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -72,6 +83,7 @@ public abstract class TextBoxBase extends AndroidViewComponent
public TextBoxBase(ComponentContainer container, EditText textview) { public TextBoxBase(ComponentContainer container, EditText textview) {
super(container); super(container);
view = textview; view = textview;
hintColorDefault = view.getCurrentHintTextColor();
// There appears to be an issue where, by default, Android 7+ // There appears to be an issue where, by default, Android 7+
// wants to provide suggestions in text boxes. However, we do not // wants to provide suggestions in text boxes. However, we do not
// compile the necessary layouts for this to work correctly, which // compile the necessary layouts for this to work correctly, which
...@@ -104,14 +116,21 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -104,14 +116,21 @@ public abstract class TextBoxBase extends AndroidViewComponent
// only). Maybe we need another color value which would be 'SYSTEM_DEFAULT' which // only). Maybe we need another color value which would be 'SYSTEM_DEFAULT' which
// will not attempt to explicitly initialize with any of the properties with any // will not attempt to explicitly initialize with any of the properties with any
// particular value. // particular value.
// BackgroundColor(Component.COLOR_NONE);
Enabled(true); Enabled(true);
fontTypeface = Component.TYPEFACE_DEFAULT; fontTypeface = Component.TYPEFACE_DEFAULT;
TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic); TextViewUtil.setFontTypeface(view, fontTypeface, bold, italic);
FontSize(Component.FONT_DEFAULT_SIZE); FontSize(Component.FONT_DEFAULT_SIZE);
Hint(""); Hint("");
if (isHighContrast || container.$form().HighContrast()) {
view.setHintTextColor(COLOR_YELLOW);
}
else {
view.setHintTextColor(hintColorDefault);
}
Text(""); Text("");
TextColor(Component.COLOR_DEFAULT); TextColor(Component.COLOR_DEFAULT);
BackgroundColor(Component.COLOR_DEFAULT);
} }
@Override @Override
...@@ -220,7 +239,11 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -220,7 +239,11 @@ public abstract class TextBoxBase extends AndroidViewComponent
if (argb != Component.COLOR_DEFAULT) { if (argb != Component.COLOR_DEFAULT) {
TextViewUtil.setBackgroundColor(view, argb); TextViewUtil.setBackgroundColor(view, argb);
} else { } else {
ViewUtil.setBackgroundDrawable(view, defaultTextBoxDrawable); if (isHighContrast || container.$form().$form().HighContrast()) {
TextViewUtil.setBackgroundColor(view, Component.COLOR_BLACK);
} else {
ViewUtil.setBackgroundDrawable(view, defaultTextBoxDrawable);
}
} }
} }
...@@ -335,7 +358,17 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -335,7 +358,17 @@ public abstract class TextBoxBase extends AndroidViewComponent
defaultValue = Component.FONT_DEFAULT_SIZE + "") defaultValue = Component.FONT_DEFAULT_SIZE + "")
@SimpleProperty @SimpleProperty
public void FontSize(float size) { public void FontSize(float size) {
TextViewUtil.setFontSize(view, size); if (Math.abs(size-Component.FONT_DEFAULT_SIZE)<.01 || Math.abs(size-24)<.01) {
if (container.$form().BigDefaultText()) {
TextViewUtil.setFontSize(view, 24);
}
else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
else {
TextViewUtil.setFontSize(view, size);
}
} }
/** /**
...@@ -456,14 +489,19 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -456,14 +489,19 @@ public abstract class TextBoxBase extends AndroidViewComponent
* @param argb text RGB color with alpha * @param argb text RGB color with alpha
*/ */
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_COLOR, @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_COLOR,
defaultValue = Component.DEFAULT_VALUE_COLOR_BLACK) defaultValue = Component.DEFAULT_VALUE_COLOR_DEFAULT)
@SimpleProperty @SimpleProperty
public void TextColor(int argb) { public void TextColor(int argb) {
textColor = argb; textColor = argb;
if (argb != Component.COLOR_DEFAULT) { if (argb != Component.COLOR_DEFAULT) {
TextViewUtil.setTextColor(view, argb); TextViewUtil.setTextColor(view, argb);
} else { } else {
TextViewUtil.setTextColor(view, container.$form().isDarkTheme() ? COLOR_WHITE : Component.COLOR_BLACK); if (isHighContrast || container.$form().HighContrast()) {
TextViewUtil.setTextColor(view, COLOR_WHITE);
}
else {
TextViewUtil.setTextColor(view, container.$form().isDarkTheme() ? COLOR_WHITE : Component.COLOR_BLACK);
}
} }
} }
...@@ -491,4 +529,52 @@ public abstract class TextBoxBase extends AndroidViewComponent ...@@ -491,4 +529,52 @@ public abstract class TextBoxBase extends AndroidViewComponent
LostFocus(); LostFocus();
} }
} }
@Override
public void setHighContrast(boolean isHighContrast) {
//background of button
if (backgroundColor == Component.COLOR_DEFAULT) {
if (isHighContrast) {
TextViewUtil.setBackgroundColor(view, Component.COLOR_BLACK);
}
else {
ViewUtil.setBackgroundDrawable(view, defaultTextBoxDrawable);
}
}
//color of text
if (textColor == Component.COLOR_DEFAULT) {
if (isHighContrast) {
TextViewUtil.setTextColor(view, COLOR_WHITE);
view.setHintTextColor(COLOR_YELLOW);
}
else {
TextViewUtil.setTextColor(view, container.$form().isDarkTheme() ? COLOR_WHITE : Component.COLOR_BLACK);
view.setHintTextColor(hintColorDefault);
}
}
}
@Override
public boolean getHighContrast() {
return isHighContrast;
}
@Override
public void setLargeFont(boolean isLargeFont) {
if (TextViewUtil.getFontSize(view, container.$context()) == 24.0 || TextViewUtil.getFontSize(view, container.$context()) == Component.FONT_DEFAULT_SIZE) {
if (isLargeFont) {
TextViewUtil.setFontSize(view, 24);
} else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
}
@Override
public boolean getLargeFont() {
return isBigText;
}
} }
...@@ -27,7 +27,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener; ...@@ -27,7 +27,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
*/ */
@SimpleObject @SimpleObject
public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewComponent public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewComponent
implements OnCheckedChangeListener, OnFocusChangeListener { implements OnCheckedChangeListener, OnFocusChangeListener, AccessibleComponent {
protected T view; protected T view;
...@@ -46,6 +46,9 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo ...@@ -46,6 +46,9 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo
// Backing for text color // Backing for text color
private int textColor; private int textColor;
// Whether the text is big or not
private boolean isBigText = false;
/** /**
* Creates a new ToggleBase component. * Creates a new ToggleBase component.
* *
...@@ -79,6 +82,33 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo ...@@ -79,6 +82,33 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo
return view; return view;
} }
@Override
public void setHighContrast(boolean isHighContrast) {
}
@Override
public boolean getHighContrast() {
return false;
}
@Override
public void setLargeFont(boolean isLargeFont) {
if (TextViewUtil.getFontSize(view, container.$context()) == 24.0 || TextViewUtil.getFontSize(view, container.$context()) == Component.FONT_DEFAULT_SIZE) {
if (isLargeFont) {
TextViewUtil.setFontSize(view, 24);
} else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
}
@Override
public boolean getLargeFont() {
return isBigText;
}
/** /**
* User tapped and released the `%type%`. * User tapped and released the `%type%`.
*/ */
...@@ -231,7 +261,17 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo ...@@ -231,7 +261,17 @@ public abstract class ToggleBase<T extends CompoundButton> extends AndroidViewCo
@SimpleProperty(description = "Specifies the text font size of the %type% in scale-independent " @SimpleProperty(description = "Specifies the text font size of the %type% in scale-independent "
+ "pixels.") + "pixels.")
public void FontSize(float size) { public void FontSize(float size) {
TextViewUtil.setFontSize(view, size); if (Math.abs(size-Component.FONT_DEFAULT_SIZE)<.01 || Math.abs(size-24)<.01) {
if (isBigText || container.$form().BigDefaultText()) {
TextViewUtil.setFontSize(view, 24);
}
else {
TextViewUtil.setFontSize(view, Component.FONT_DEFAULT_SIZE);
}
}
else {
TextViewUtil.setFontSize(view, size);
}
} }
/** /**
......
...@@ -410,6 +410,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd> ...@@ -410,6 +410,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd>
<h3 id="Image-Properties">Properties</h3> <h3 id="Image-Properties">Properties</h3>
<dl class="properties"> <dl class="properties">
<dt id="Image.AlternateText" class="text wo"><em>AlternateText</em></dt>
<dd>A written description of what the image looks like.</dd>
<dt id="Image.Animation" class="text wo bo"><em>Animation</em></dt> <dt id="Image.Animation" class="text wo bo"><em>Animation</em></dt>
<dd>This is a limited form of animation that can attach a small number of motion types to images. <dd>This is a limited form of animation that can attach a small number of motion types to images.
The allowable motions are <code class="highlighter-rouge">ScrollRightSlow</code>, <code class="highlighter-rouge">ScrollRight</code>, <code class="highlighter-rouge">ScrollRightFast</code>, The allowable motions are <code class="highlighter-rouge">ScrollRightSlow</code>, <code class="highlighter-rouge">ScrollRight</code>, <code class="highlighter-rouge">ScrollRightFast</code>,
...@@ -887,6 +889,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd> ...@@ -887,6 +889,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd>
<dt id="Screen.BackgroundImage" class="text"><em>BackgroundImage</em></dt> <dt id="Screen.BackgroundImage" class="text"><em>BackgroundImage</em></dt>
<dd>Specifies the path of the <code class="highlighter-rouge">Screen</code>’s background image. If there is both an <code class="highlighter-rouge">BackgroundImage</code> <dd>Specifies the path of the <code class="highlighter-rouge">Screen</code>’s background image. If there is both an <code class="highlighter-rouge">BackgroundImage</code>
and a <a href="#Screen.BackgroundColor"><code class="highlighter-rouge">BackgroundColor</code></a> specified, only the <code class="highlighter-rouge">BackgroundImage</code> will be visible.</dd> and a <a href="#Screen.BackgroundColor"><code class="highlighter-rouge">BackgroundColor</code></a> specified, only the <code class="highlighter-rouge">BackgroundImage</code> will be visible.</dd>
<dt id="Screen.BigDefaultText" class="boolean"><em>BigDefaultText</em></dt>
<dd>When checked, all default size text will be increased in size.</dd>
<dt id="Screen.BlocksToolkit" class="text wo do"><em>BlocksToolkit</em></dt> <dt id="Screen.BlocksToolkit" class="text wo do"><em>BlocksToolkit</em></dt>
<dd>A JSON string representing the subset for the screen. Authors of template apps can use this to control what components, designer properties, and blocks are available in the project.</dd> <dd>A JSON string representing the subset for the screen. Authors of template apps can use this to control what components, designer properties, and blocks are available in the project.</dd>
<dt id="Screen.CloseScreenAnimation" class="text"><em>CloseScreenAnimation</em></dt> <dt id="Screen.CloseScreenAnimation" class="text"><em>CloseScreenAnimation</em></dt>
...@@ -894,6 +898,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd> ...@@ -894,6 +898,8 @@ Valid values for the month field are 1-12 and 1-31 for the day field.</dd>
to a form behind it in the activity stack.</dd> to a form behind it in the activity stack.</dd>
<dt id="Screen.Height" class="number ro bo"><em>Height</em></dt> <dt id="Screen.Height" class="number ro bo"><em>Height</em></dt>
<dd>Returns the Screen height in pixels (y-size).</dd> <dd>Returns the Screen height in pixels (y-size).</dd>
<dt id="Screen.HighContrast" class="boolean"><em>HighContrast</em></dt>
<dd>When checked, there will be high contrast mode turned on.</dd>
<dt id="Screen.Icon" class="text wo do"><em>Icon</em></dt> <dt id="Screen.Icon" class="text wo do"><em>Icon</em></dt>
<dd>The image used for your App’s display icon should be a square png or jpeg image with dimensions <dd>The image used for your App’s display icon should be a square png or jpeg image with dimensions
up to 1024x1024 pixels. Larger images may cause compiling or installing the app to fail. up to 1024x1024 pixels. Larger images may cause compiling or installing the app to fail.
......
...@@ -362,6 +362,9 @@ Component for displaying images and basic animations. ...@@ -362,6 +362,9 @@ Component for displaying images and basic animations.
{:.properties} {:.properties}
{:id="Image.AlternateText" .text .wo} *AlternateText*
: A written description of what the image looks like.
{:id="Image.Animation" .text .wo .bo} *Animation* {:id="Image.Animation" .text .wo .bo} *Animation*
: This is a limited form of animation that can attach a small number of motion types to images. : This is a limited form of animation that can attach a small number of motion types to images.
The allowable motions are `ScrollRightSlow`, `ScrollRight`, `ScrollRightFast`, The allowable motions are `ScrollRightSlow`, `ScrollRight`, `ScrollRightFast`,
...@@ -963,6 +966,9 @@ Top-level component containing all other components in the program. ...@@ -963,6 +966,9 @@ Top-level component containing all other components in the program.
: Specifies the path of the `Screen`'s background image. If there is both an `BackgroundImage` : Specifies the path of the `Screen`'s background image. If there is both an `BackgroundImage`
and a [`BackgroundColor`](#Screen.BackgroundColor) specified, only the `BackgroundImage` will be visible. and a [`BackgroundColor`](#Screen.BackgroundColor) specified, only the `BackgroundImage` will be visible.
{:id="Screen.BigDefaultText" .boolean} *BigDefaultText*
: When checked, all default size text will be increased in size.
{:id="Screen.BlocksToolkit" .text .wo .do} *BlocksToolkit* {:id="Screen.BlocksToolkit" .text .wo .do} *BlocksToolkit*
: A JSON string representing the subset for the screen. Authors of template apps can use this to control what components, designer properties, and blocks are available in the project. : A JSON string representing the subset for the screen. Authors of template apps can use this to control what components, designer properties, and blocks are available in the project.
...@@ -973,6 +979,9 @@ Top-level component containing all other components in the program. ...@@ -973,6 +979,9 @@ Top-level component containing all other components in the program.
{:id="Screen.Height" .number .ro .bo} *Height* {:id="Screen.Height" .number .ro .bo} *Height*
: Returns the Screen height in pixels (y-size). : Returns the Screen height in pixels (y-size).
{:id="Screen.HighContrast" .boolean} *HighContrast*
: When checked, there will be high contrast mode turned on.
{:id="Screen.Icon" .text .wo .do} *Icon* {:id="Screen.Icon" .text .wo .do} *Icon*
: The image used for your App's display icon should be a square png or jpeg image with dimensions : The image used for your App's display icon should be a square png or jpeg image with dimensions
up to 1024x1024 pixels. Larger images may cause compiling or installing the app to fail. up to 1024x1024 pixels. Larger images may cause compiling or installing the app to fail.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment