/* * Parses the JSON properties and upgrades the component if necessary. * This method is called recursively for nested components. */ private static void upgradeComponent( int srcYaVersion, Map<String, JSONValue> componentProperties, StringBuilder upgradeDetails) { String componentType = componentProperties.get("$Type").asString().getString(); // Get the source component version from the componentProperties. int srcCompVersion = 0; if (componentProperties.containsKey("$Version")) { String version = componentProperties.get("$Version").asString().getString(); srcCompVersion = Integer.parseInt(version); } if (srcYaVersion < 2) { // In YOUNG_ANDROID_VERSION 2, the Logger component was removed; Notifier should be used // instead. // Here we change the Logger component to a Notifier component automatically. Sweet! // (We need to do this upgrade here, not in the upgradeComponentProperties method. This is // because the code below calls COMPONENT_DATABASE.getComponentVersion() and that will fail // if componentType is "Logger" because "Logger" isn't a valid component type anymore.) if (componentType.equals("Logger")) { componentType = "Notifier"; srcCompVersion = COMPONENT_DATABASE.getComponentVersion(componentType); componentProperties.put("$Type", new ClientJsonString(componentType)); componentProperties.put("$Version", new ClientJsonString("" + srcCompVersion)); upgradeDetails.append( MESSAGES.upgradeDetailLoggerReplacedWithNotifier( componentProperties.get("$Name").asString().getString())); } } // Get the system component version from the component database. final int sysCompVersion = COMPONENT_DATABASE.getComponentVersion(componentType); // Upgrade if necessary. upgradeComponentProperties(componentProperties, componentType, srcCompVersion, sysCompVersion); if (srcYaVersion < 26) { // Beginning with YOUNG_ANDROID_VERSION 26: // - In .scm files, values for asset, BluetoothClient, component, lego_nxt_sensor_port, // and string properties no longer contain leading and trailing quotes. unquotePropertyValues(componentProperties, componentType); } // Upgrade nested components if (componentProperties.containsKey("$Components")) { JSONArray componentsArray = componentProperties.get("$Components").asArray(); for (JSONValue nestedComponent : componentsArray.getElements()) { upgradeComponent(srcYaVersion, nestedComponent.asObject().getProperties(), upgradeDetails); } } }
/** Instantiates mock component by name. */ public static MockComponent createMockComponent(String name, SimpleEditor editor) { if (COMPONENT_DATABASE.getNonVisible(name)) { return new MockNonVisibleComponent( editor, name, getImageFromPath(COMPONENT_DATABASE.getIconName(name))); } else if (name.equals(MockButton.TYPE)) { return new MockButton(editor); } else if (name.equals(MockCanvas.TYPE)) { return new MockCanvas(editor); } else if (name.equals(MockCheckBox.TYPE)) { return new MockCheckBox(editor); } else if (name.equals(MockImage.TYPE)) { return new MockImage(editor); } else if (name.equals(MockLabel.TYPE)) { return new MockLabel(editor); } else if (name.equals(MockPasswordTextBox.TYPE)) { return new MockPasswordTextBox(editor); } else if (name.equals(MockRadioButton.TYPE)) { return new MockRadioButton(editor); } else if (name.equals(MockTextBox.TYPE)) { return new MockTextBox(editor); } else if (name.equals(MockContactPicker.TYPE)) { return new MockContactPicker(editor); } else if (name.equals(MockPhoneNumberPicker.TYPE)) { return new MockPhoneNumberPicker(editor); } else if (name.equals(MockEmailPicker.TYPE)) { return new MockEmailPicker(editor); } else if (name.equals(MockListPicker.TYPE)) { return new MockListPicker(editor); } else if (name.equals(MockHorizontalArrangement.TYPE)) { return new MockHorizontalArrangement(editor); } else if (name.equals(MockVerticalArrangement.TYPE)) { return new MockVerticalArrangement(editor); } else if (name.equals(MockTableArrangement.TYPE)) { return new MockTableArrangement(editor); } else if (name.equals(MockImageSprite.TYPE)) { return new MockImageSprite(editor); } else if (name.equals(MockBall.TYPE)) { return new MockBall(editor); } else if (name.equals(MockImagePicker.TYPE)) { return new MockImagePicker(editor); } else if (name.equals(MockVideoPlayer.TYPE)) { return new MockVideoPlayer(editor); } else if (name.equals(MockWebViewer.TYPE)) { return new MockWebViewer(editor); } else { // TODO(user): add 3rd party mock component proxy here throw new UnsupportedOperationException("unknown component: " + name); } }
/** * Returns an image for display on the component palette. * * @return image for component */ public Image getImage() { if (nonVisible) { return getImageFromPath(COMPONENT_DATABASE.getIconName(name)); } else { return getCachedMockComponent(name, editor).getIconImage(); } }
@Override public void configureComponent(MockComponent mockComponent) { String componentType = mockComponent.getType(); // Configure properties for (PropertyDefinition property : COMPONENT_DATABASE.getPropertyDefinitions(componentType)) { mockComponent.addProperty( property.getName(), property.getDefaultValue(), property.getCaption(), createPropertyEditor(property.getEditorType())); } }
private static void unquotePropertyValues( Map<String, JSONValue> componentProperties, String componentType) { // From the component database, get the map of property names and types for the component type. Map<String, String> propertyTypesByName = COMPONENT_DATABASE.getPropertyTypesByName(componentType); // Iterate through the component properties. for (String propertyName : componentProperties.keySet()) { // Get the property type. String propertyType = propertyTypesByName.get(propertyName); // In theory the check for propertyType == null shouldn't be necessary // but I have sometimes had a problem with it being null when running // with GWT debugging. Maybe it changes the timing somehow. Anyway, // this test for null should not hurt anything. -Sharon if (propertyType == null) { OdeLog.wlog( "Couldn't find propertyType for property " + propertyName + " in component type " + componentType); continue; } // If the property type is one that was previously quoted, unquote the value. if (propertyType.equals("asset") || propertyType.equals("BluetoothClient") || propertyType.equals("component") || propertyType.equals("lego_nxt_sensor_port") || propertyType.equals("string")) { // Unquote the property value. JSONValue propertyValue = componentProperties.get(propertyName); String propertyValueString = propertyValue.asString().getString(); propertyValueString = StringUtils.unquote(propertyValueString); componentProperties.put(propertyName, new ClientJsonString(propertyValueString)); } } }
/** * Loads all components to be shown on this palette. Specifically, for each component (except for * those whose category is UNINITIALIZED, or whose category is INTERNAL and we're running on a * production server, or who are specifically marked as not to be shown on the palette), this * creates a corresponding {@link SimplePaletteItem} with the passed {@link DropTargetProvider} * and adds it to the panel corresponding to its category. * * @param dropTargetProvider provider of targets that palette items can be dropped on */ @Override public void loadComponents(DropTargetProvider dropTargetProvider) { for (String component : COMPONENT_DATABASE.getComponentNames()) { String categoryString = COMPONENT_DATABASE.getCategoryString(component); String helpString = COMPONENT_DATABASE.getHelpString(component); String categoryDocUrlString = COMPONENT_DATABASE.getCategoryDocUrlString(component); Boolean showOnPalette = COMPONENT_DATABASE.getShowOnPalette(component); Boolean nonVisible = COMPONENT_DATABASE.getNonVisible(component); ComponentCategory category = ComponentCategory.valueOf(categoryString); if (showOnPalette && showCategory(category)) { addPaletteItem( new SimplePaletteItem( new SimpleComponentDescriptor( component, editor, helpString, categoryDocUrlString, showOnPalette, nonVisible), dropTargetProvider), category); } } }
/** * Panel showing Simple components which can be dropped onto the Young Android visual designer * panel. * * @author [email protected] (Liz Looney) */ public class YoungAndroidPalettePanel extends Composite implements SimplePalettePanel { // Component database: information about components (including their properties and events) private static final SimpleComponentDatabase COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(); // Associated editor private final YaFormEditor editor; private final StackPanel stackPalette; private final Map<ComponentCategory, VerticalPanel> categoryPanels; /** * Creates a new component palette panel. * * @param editor parent editor of this panel */ public YoungAndroidPalettePanel(YaFormEditor editor) { this.editor = editor; stackPalette = new StackPanel(); categoryPanels = new HashMap<ComponentCategory, VerticalPanel>(); for (ComponentCategory category : ComponentCategory.values()) { if (showCategory(category)) { VerticalPanel categoryPanel = new VerticalPanel(); categoryPanel.setWidth("100%"); categoryPanels.put(category, categoryPanel); stackPalette.add(categoryPanel, category.getName()); } } stackPalette.setWidth("100%"); initWidget(stackPalette); } private static boolean showCategory(ComponentCategory category) { if (category == ComponentCategory.UNINITIALIZED) { return false; } if (category == ComponentCategory.INTERNAL && !AppInventorFeatures.showInternalComponentsCategory()) { return false; } return true; } /** * Loads all components to be shown on this palette. Specifically, for each component (except for * those whose category is UNINITIALIZED, or whose category is INTERNAL and we're running on a * production server, or who are specifically marked as not to be shown on the palette), this * creates a corresponding {@link SimplePaletteItem} with the passed {@link DropTargetProvider} * and adds it to the panel corresponding to its category. * * @param dropTargetProvider provider of targets that palette items can be dropped on */ @Override public void loadComponents(DropTargetProvider dropTargetProvider) { for (String component : COMPONENT_DATABASE.getComponentNames()) { String categoryString = COMPONENT_DATABASE.getCategoryString(component); String helpString = COMPONENT_DATABASE.getHelpString(component); String categoryDocUrlString = COMPONENT_DATABASE.getCategoryDocUrlString(component); Boolean showOnPalette = COMPONENT_DATABASE.getShowOnPalette(component); Boolean nonVisible = COMPONENT_DATABASE.getNonVisible(component); ComponentCategory category = ComponentCategory.valueOf(categoryString); if (showOnPalette && showCategory(category)) { addPaletteItem( new SimplePaletteItem( new SimpleComponentDescriptor( component, editor, helpString, categoryDocUrlString, showOnPalette, nonVisible), dropTargetProvider), category); } } } @Override public void configureComponent(MockComponent mockComponent) { String componentType = mockComponent.getType(); // Configure properties for (PropertyDefinition property : COMPONENT_DATABASE.getPropertyDefinitions(componentType)) { mockComponent.addProperty( property.getName(), property.getDefaultValue(), property.getCaption(), createPropertyEditor(property.getEditorType())); } } /* * Creates a new property editor. */ private PropertyEditor createPropertyEditor(String editorType) { if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_HORIZONTAL_ALIGNMENT)) { return new YoungAndroidHorizontalAlignmentChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_VERTICAL_ALIGNMENT)) { return new YoungAndroidVerticalAlignmentChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_ASSET)) { return new YoungAndroidAssetSelectorPropertyEditor(editor); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_BLUETOOTHCLIENT)) { return new YoungAndroidComponentSelectorPropertyEditor( editor, // Pass the set of component types that will be shown in the property editor, // in this case, just "BluetoothClient". Collections.singleton("BluetoothClient")); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN)) { return new YoungAndroidBooleanPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_BUTTON_SHAPE)) { return new YoungAndroidButtonShapeChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_COLOR)) { return new YoungAndroidColorChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_COMPONENT)) { return new YoungAndroidComponentSelectorPropertyEditor(editor); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_FLOAT)) { return new FloatPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_INTEGER)) { return new IntegerPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_LEGO_NXT_SENSOR_PORT)) { return new YoungAndroidLegoNxtSensorPortChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_LEGO_NXT_GENERATED_COLOR)) { return new YoungAndroidColorChoicePropertyEditor( YoungAndroidColorChoicePropertyEditor.NXT_GENERATED_COLORS); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_FLOAT)) { return new NonNegativeFloatPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_INTEGER)) { return new NonNegativeIntegerPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_SCREEN_ORIENTATION)) { return new YoungAndroidScreenOrientationChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_SCREEN_ANIMATION)) { return new YoungAndroidScreenAnimationChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_SENSOR_DIST_INTERVAL)) { return new YoungAndroidSensorDistIntervalChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_SENSOR_TIME_INTERVAL)) { return new YoungAndroidSensorTimeIntervalChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_STRING)) { return new StringPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_TEXTALIGNMENT)) { return new YoungAndroidAlignmentChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_TEXTAREA)) { return new TextAreaPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_TOAST_LENGTH)) { return new YoungAndroidToastLengthChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_TYPEFACE)) { return new YoungAndroidFontTypefaceChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_VISIBILITY)) { return new YoungAndroidVisibilityChoicePropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_TEXT_RECEIVING)) { return new YoungAndroidTextReceivingPropertyEditor(); } else if (editorType.equals(PropertyTypeConstants.PROPERTY_TYPE_ACCELEROMETER_SENSITIVITY)) { return new YoungAndroidAccelerometerSensitivityChoicePropertyEditor(); } else { return new TextPropertyEditor(); } } /* * Adds a component entry to the palette. */ private void addPaletteItem(SimplePaletteItem component, ComponentCategory category) { VerticalPanel panel = categoryPanels.get(category); panel.add(component); } }
/** Descriptor for components on the component palette panel. This class is immutable. */ public final class SimpleComponentDescriptor { // Component display name private final String name; private final SimpleEditor editor; // Help information to display for component private final String helpString; // Goro documentation category URL piece private final String categoryDocUrlString; // Whether to show the component on the palette private final boolean showOnPalette; // Whether the component has a visual representation in the app's UI private final boolean nonVisible; // an instantiated mockcomponent is currently necessary in order to // to get the image, category, and description private MockComponent cachedMockComponent = null; // Component database: information about components (including their properties and events) private static final SimpleComponentDatabase COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(); /* We keep a static map of image names to images in the image bundle so * that we can avoid making individual calls to the server for static image * that are already in the bundle. This is purely an efficiency optimization * for mock non-visible components. */ private static final Images images = Ode.getImageBundle(); private static final Map<String, ImageResource> bundledImages = Maps.newHashMap(); private static boolean imagesInitialized = false; private static void initBundledImages() { bundledImages.put("images/accelerometersensor.png", images.accelerometersensor()); bundledImages.put("images/activityStarter.png", images.activitystarter()); bundledImages.put("images/barcodeScanner.png", images.barcodeScanner()); bundledImages.put("images/bluetooth.png", images.bluetooth()); bundledImages.put("images/camera.png", images.camera()); bundledImages.put("images/camcorder.png", images.camcorder()); bundledImages.put("images/clock.png", images.clock()); bundledImages.put("images/fusiontables.png", images.fusiontables()); bundledImages.put("images/gameClient.png", images.gameclient()); bundledImages.put("images/locationSensor.png", images.locationSensor()); bundledImages.put("images/notifier.png", images.notifier()); bundledImages.put("images/legoMindstormsNxt.png", images.legoMindstormsNxt()); bundledImages.put("images/orientationsensor.png", images.orientationsensor()); bundledImages.put("images/pedometer.png", images.pedometerComponent()); bundledImages.put("images/phoneCall.png", images.phonecall()); bundledImages.put("images/player.png", images.player()); bundledImages.put("images/soundEffect.png", images.soundeffect()); bundledImages.put("images/soundRecorder.png", images.soundRecorder()); bundledImages.put("images/speechRecognizer.png", images.speechRecognizer()); bundledImages.put("images/textToSpeech.png", images.textToSpeech()); bundledImages.put("images/texting.png", images.texting()); bundledImages.put("images/tinyDB.png", images.tinyDB()); bundledImages.put("images/tinyWebDB.png", images.tinyWebDB()); bundledImages.put("images/twitter.png", images.twitterComponent()); bundledImages.put("images/voting.png", images.voting()); bundledImages.put("images/web.png", images.web()); imagesInitialized = true; } /** * Creates a new component descriptor. * * @param name component display name */ public SimpleComponentDescriptor( String name, SimpleEditor editor, String helpString, String categoryDocUrlString, boolean showOnPalette, boolean nonVisible) { this.name = name; this.editor = editor; this.helpString = helpString; this.categoryDocUrlString = categoryDocUrlString; this.showOnPalette = showOnPalette; this.nonVisible = nonVisible; } /** * Returns the display name of the component. * * @return component display name */ public String getName() { return name; } /** * Returns the help string for the component. For more detail, see javadoc for {@link * com.google.appinventor.client.editor.simple.ComponentDatabase#getHelpString(String)}. * * @return helpful message about the component */ public String getHelpString() { return helpString; } /** * Returns the categoryDocUrl string for the component. For more detail, see javadoc for {@link * com.google.appinventor.client.editor.simple.ComponentDatabase#getCategoryDocUrlString(String)}. * * @return helpful message about the component */ public String getCategoryDocUrlString() { return categoryDocUrlString; } /** * Returns whether this component should be shown on the palette. For more detail, see javadoc for * {@link com.google.appinventor.client.editor.simple.ComponentDatabase#getHelpString(String)}. * * @return whether the component should be shown on the palette */ public boolean getShowOnPalette() { return showOnPalette; } /** * Returns whether this component is visible in the app's UI. For more detail, see javadoc for * {@link com.google.appinventor.client.editor.simple.ComponentDatabase#getHelpString(String)}. * * @return whether the component is non-visible */ public boolean getNonVisible() { return nonVisible; } /** * Returns an image for display on the component palette. * * @return image for component */ public Image getImage() { if (nonVisible) { return getImageFromPath(COMPONENT_DATABASE.getIconName(name)); } else { return getCachedMockComponent(name, editor).getIconImage(); } } /** * Returns a draggable image for the component. Used when dragging a component from the palette * onto the form. * * @return draggable widget for component */ public Widget getDragWidget() { return createMockComponent(name, editor); } /** * Instantiates the corresponding mock component. * * @return mock component */ public MockComponent createMockComponentFromPalette() { MockComponent mockComponent = createMockComponent(name, editor); mockComponent.onCreateFromPalette(); return mockComponent; } /** Gets cached mock component; creates if necessary. */ private MockComponent getCachedMockComponent(String name, SimpleEditor editor) { if (cachedMockComponent == null) { cachedMockComponent = createMockComponent(name, editor); } return cachedMockComponent; } public static Image getImageFromPath(String iconPath) { if (!imagesInitialized) { initBundledImages(); } if (bundledImages.containsKey(iconPath)) { return new Image(bundledImages.get(iconPath)); } else { return new Image(iconPath); } } /** Instantiates mock component by name. */ public static MockComponent createMockComponent(String name, SimpleEditor editor) { if (COMPONENT_DATABASE.getNonVisible(name)) { return new MockNonVisibleComponent( editor, name, getImageFromPath(COMPONENT_DATABASE.getIconName(name))); } else if (name.equals(MockButton.TYPE)) { return new MockButton(editor); } else if (name.equals(MockCanvas.TYPE)) { return new MockCanvas(editor); } else if (name.equals(MockCheckBox.TYPE)) { return new MockCheckBox(editor); } else if (name.equals(MockImage.TYPE)) { return new MockImage(editor); } else if (name.equals(MockLabel.TYPE)) { return new MockLabel(editor); } else if (name.equals(MockPasswordTextBox.TYPE)) { return new MockPasswordTextBox(editor); } else if (name.equals(MockRadioButton.TYPE)) { return new MockRadioButton(editor); } else if (name.equals(MockTextBox.TYPE)) { return new MockTextBox(editor); } else if (name.equals(MockContactPicker.TYPE)) { return new MockContactPicker(editor); } else if (name.equals(MockPhoneNumberPicker.TYPE)) { return new MockPhoneNumberPicker(editor); } else if (name.equals(MockEmailPicker.TYPE)) { return new MockEmailPicker(editor); } else if (name.equals(MockListPicker.TYPE)) { return new MockListPicker(editor); } else if (name.equals(MockHorizontalArrangement.TYPE)) { return new MockHorizontalArrangement(editor); } else if (name.equals(MockVerticalArrangement.TYPE)) { return new MockVerticalArrangement(editor); } else if (name.equals(MockTableArrangement.TYPE)) { return new MockTableArrangement(editor); } else if (name.equals(MockImageSprite.TYPE)) { return new MockImageSprite(editor); } else if (name.equals(MockBall.TYPE)) { return new MockBall(editor); } else if (name.equals(MockImagePicker.TYPE)) { return new MockImagePicker(editor); } else if (name.equals(MockVideoPlayer.TYPE)) { return new MockVideoPlayer(editor); } else if (name.equals(MockWebViewer.TYPE)) { return new MockWebViewer(editor); } else { // TODO(user): add 3rd party mock component proxy here throw new UnsupportedOperationException("unknown component: " + name); } } }
/** * A class that can upgrade a Young Android Form source file. * * @author [email protected] (Liz Looney) */ public final class YoungAndroidFormUpgrader { static class LoadException extends IllegalStateException { LoadException(String message) { super(message); } } private static final SimpleComponentDatabase COMPONENT_DATABASE = SimpleComponentDatabase.getInstance(); private YoungAndroidFormUpgrader() {} /** * Upgrades the given sourceProperties if necessary. * * @param sourceProperties the properties from the source file * @return true if the sourceProperties was upgraded, false otherwise */ public static boolean upgradeSourceProperties(Map<String, JSONValue> sourceProperties) { StringBuilder upgradeDetails = new StringBuilder(); try { int srcYaVersion = getSrcYaVersion(sourceProperties); if (needToUpgrade(srcYaVersion)) { Map<String, JSONValue> formProperties = sourceProperties.get("Properties").asObject().getProperties(); upgradeComponent(srcYaVersion, formProperties, upgradeDetails); // The sourceProperties were upgraded. Update the version number. setSrcYaVersion(sourceProperties); if (upgradeDetails.length() > 0) { Window.alert(MESSAGES.projectWasUpgraded(upgradeDetails.toString())); } return true; } } catch (LoadException e) { // This shouldn't happen. If it does it's our fault, not the user's fault. Window.alert(MESSAGES.unexpectedProblem(e.getMessage())); OdeLog.xlog(e); } return false; } private static int getSrcYaVersion(Map<String, JSONValue> sourceProperties) { int srcYaVersion = 0; if (sourceProperties.containsKey("YaVersion")) { String version = sourceProperties.get("YaVersion").asString().getString(); srcYaVersion = Integer.parseInt(version); } return srcYaVersion; } private static void setSrcYaVersion(Map<String, JSONValue> sourceProperties) { sourceProperties.put("YaVersion", new ClientJsonString("" + YaVersion.YOUNG_ANDROID_VERSION)); } private static boolean needToUpgrade(int srcYaVersion) { // Compare the source file's YoungAndroid version with the system's YoungAndroid version. final int sysYaVersion = YaVersion.YOUNG_ANDROID_VERSION; if (srcYaVersion > sysYaVersion) { // The source file's version is newer than the system's version. // This can happen if the user is using (or in the past has used) a non-production version of // App Inventor. // This can also happen if the user is connected to a new version of App Inventor and then // later is connected to an old version of App Inventor. // We'll try to load the project but there may be compatibility issues if the project uses // future components or other features that the current system doesn't understand. Window.alert(MESSAGES.newerVersionProject()); return false; } if (srcYaVersion == 0) { // The source file doesn't have a YoungAndroid version number. // There are two situations that cause this: // 1. The project may have been downloaded from alpha (androidblocks.googlelabs.com) and // uploaded to beta (appinventor.googlelabs.com), which is illegal. // 2. The project may have been created with beta (appinventor.googlelabs.com) before we // started putting version numbers into the source file, which is legal, and nothing // really changed between version 0 and version 1. // // For a limited time, we assume #2, show a warning, and proceed. // TODO(lizlooney) - after the limited time is up (when we think that all appinventor // projects have been upgraded), we may decide to refuse to load the project. Window.alert(MESSAGES.veryOldProject()); } return (srcYaVersion < sysYaVersion); } /* * Parses the JSON properties and upgrades the component if necessary. * This method is called recursively for nested components. */ private static void upgradeComponent( int srcYaVersion, Map<String, JSONValue> componentProperties, StringBuilder upgradeDetails) { String componentType = componentProperties.get("$Type").asString().getString(); // Get the source component version from the componentProperties. int srcCompVersion = 0; if (componentProperties.containsKey("$Version")) { String version = componentProperties.get("$Version").asString().getString(); srcCompVersion = Integer.parseInt(version); } if (srcYaVersion < 2) { // In YOUNG_ANDROID_VERSION 2, the Logger component was removed; Notifier should be used // instead. // Here we change the Logger component to a Notifier component automatically. Sweet! // (We need to do this upgrade here, not in the upgradeComponentProperties method. This is // because the code below calls COMPONENT_DATABASE.getComponentVersion() and that will fail // if componentType is "Logger" because "Logger" isn't a valid component type anymore.) if (componentType.equals("Logger")) { componentType = "Notifier"; srcCompVersion = COMPONENT_DATABASE.getComponentVersion(componentType); componentProperties.put("$Type", new ClientJsonString(componentType)); componentProperties.put("$Version", new ClientJsonString("" + srcCompVersion)); upgradeDetails.append( MESSAGES.upgradeDetailLoggerReplacedWithNotifier( componentProperties.get("$Name").asString().getString())); } } // Get the system component version from the component database. final int sysCompVersion = COMPONENT_DATABASE.getComponentVersion(componentType); // Upgrade if necessary. upgradeComponentProperties(componentProperties, componentType, srcCompVersion, sysCompVersion); if (srcYaVersion < 26) { // Beginning with YOUNG_ANDROID_VERSION 26: // - In .scm files, values for asset, BluetoothClient, component, lego_nxt_sensor_port, // and string properties no longer contain leading and trailing quotes. unquotePropertyValues(componentProperties, componentType); } // Upgrade nested components if (componentProperties.containsKey("$Components")) { JSONArray componentsArray = componentProperties.get("$Components").asArray(); for (JSONValue nestedComponent : componentsArray.getElements()) { upgradeComponent(srcYaVersion, nestedComponent.asObject().getProperties(), upgradeDetails); } } } private static void upgradeComponentProperties( Map<String, JSONValue> componentProperties, String componentType, int srcCompVersion, final int sysCompVersion) { // Compare the source file's component version with the system's component version. if (srcCompVersion == 0) { // The source file doesn't have a version number for this component. // There are two situations that cause this: // 1. The project may have been downloaded from alpha (androidblocks.googlelabs.com) and // uploaded to beta (appinventor.googlelabs.com), which is illegal. // 2. The project may have been created with beta (appinventor.googlelabs.com) before we // started putting version numbers into the source file, which is legal, and nothing // really changed between version 0 and version 1. // // For a limited time, we assume #2 and pretend that the source file said version 1. // TODO(lizlooney) - after the limited time is up (when we think that all appinventor // projects have been upgraded), we may decide to refuse to load the project. srcCompVersion = 1; } if (srcCompVersion > sysCompVersion) { // This shouldn't happen because we should have already detected that the project is a newer // version than the system and returned false in needToUpgrade. // NOTE(lizlooney,user) - we need to make sure that this situation does not happen by // incrementing YaVersion.YOUNG_ANDROID_VERSION each time a component's version number is // incremented. throw new LoadException( MESSAGES.newerVersionComponentException(componentType, srcCompVersion, sysCompVersion)); } if (srcCompVersion < sysCompVersion) { // NOTE(lizlooney,user) - when a component changes, increment the component's version // number in com.google.appinventor.components.common.YaVersion and add code here to upgrade // properties as necessary. if (componentType.equals("AccelerometerSensor")) { srcCompVersion = upgradeAccelerometerSensorProperties(componentProperties, srcCompVersion); } else if (componentType.equals("ActivityStarter")) { srcCompVersion = upgradeActivityStarterProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Ball")) { srcCompVersion = upgradeBallProperties(componentProperties, srcCompVersion); } else if (componentType.equals("BluetoothClient")) { srcCompVersion = upgradeBluetoothClientProperties(componentProperties, srcCompVersion); } else if (componentType.equals("BluetoothServer")) { srcCompVersion = upgradeBluetoothServerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Button")) { srcCompVersion = upgradeButtonProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Canvas")) { srcCompVersion = upgradeCanvasProperties(componentProperties, srcCompVersion); } else if (componentType.equals("CheckBox")) { srcCompVersion = upgradeCheckBoxProperties(componentProperties, srcCompVersion); } else if (componentType.equals("ContactPicker")) { srcCompVersion = upgradeContactPickerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("EmailPicker")) { srcCompVersion = upgradeEmailPickerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Form")) { srcCompVersion = upgradeFormProperties(componentProperties, srcCompVersion); } else if (componentType.equals("HorizontalArrangement")) { srcCompVersion = upgradeHorizontalArrangementProperties(componentProperties, srcCompVersion); } else if (componentType.equals("ImagePicker")) { srcCompVersion = upgradeImagePickerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("ImageSprite")) { srcCompVersion = upgradeImageSpriteProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Label")) { srcCompVersion = upgradeLabelProperties(componentProperties, srcCompVersion); } else if (componentType.equals("ListPicker")) { srcCompVersion = upgradeListPickerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("LocationSensor")) { srcCompVersion = upgradeLocationSensorProperties(componentProperties, srcCompVersion); } else if (componentType.equals("OrientationSensor")) { srcCompVersion = upgradeOrientationSensorProperties(componentProperties, srcCompVersion); } else if (componentType.equals("PasswordTextBox")) { srcCompVersion = upgradePasswordTextBoxProperties(componentProperties, srcCompVersion); } else if (componentType.equals("PhoneNumberPicker")) { srcCompVersion = upgradePhoneNumberPickerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Player")) { srcCompVersion = upgradePlayerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Sound")) { srcCompVersion = upgradeSoundProperties(componentProperties, srcCompVersion); } else if (componentType.equals("TinyWebDB")) { srcCompVersion = upgradeTinyWebDBProperties(componentProperties, srcCompVersion); } else if (componentType.equals("VerticalArrangement")) { srcCompVersion = upgradeVerticalArrangementProperties(componentProperties, srcCompVersion); } else if (componentType.equals("VideoPlayer")) { srcCompVersion = upgradeVideoPlayerProperties(componentProperties, srcCompVersion); } else if (componentType.equals("TextBox")) { srcCompVersion = upgradeTextBoxProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Twitter")) { srcCompVersion = upgradeTwitterProperties(componentProperties, srcCompVersion); } else if (componentType.equals("Web")) { srcCompVersion = upgradeWebProperties(componentProperties, srcCompVersion); } else if (componentType.equals("WebViewer")) { srcCompVersion = upgradeWebViewerProperties(componentProperties, srcCompVersion); } if (srcCompVersion < sysCompVersion) { // If we got here, a component needed to be upgraded, but nothing handled it. // NOTE(lizlooney,user) - we need to make sure that this situation does not happen by // adding the appropriate code above to handle all component upgrades. throw new LoadException( MESSAGES.noUpgradeStrategyException(componentType, srcCompVersion, sysCompVersion)); } // The component was upgraded. Update the $Version property. componentProperties.put("$Version", new ClientJsonString("" + srcCompVersion)); } } private static void unquotePropertyValues( Map<String, JSONValue> componentProperties, String componentType) { // From the component database, get the map of property names and types for the component type. Map<String, String> propertyTypesByName = COMPONENT_DATABASE.getPropertyTypesByName(componentType); // Iterate through the component properties. for (String propertyName : componentProperties.keySet()) { // Get the property type. String propertyType = propertyTypesByName.get(propertyName); // In theory the check for propertyType == null shouldn't be necessary // but I have sometimes had a problem with it being null when running // with GWT debugging. Maybe it changes the timing somehow. Anyway, // this test for null should not hurt anything. -Sharon if (propertyType == null) { OdeLog.wlog( "Couldn't find propertyType for property " + propertyName + " in component type " + componentType); continue; } // If the property type is one that was previously quoted, unquote the value. if (propertyType.equals("asset") || propertyType.equals("BluetoothClient") || propertyType.equals("component") || propertyType.equals("lego_nxt_sensor_port") || propertyType.equals("string")) { // Unquote the property value. JSONValue propertyValue = componentProperties.get(propertyName); String propertyValueString = propertyValue.asString().getString(); propertyValueString = StringUtils.unquote(propertyValueString); componentProperties.put(propertyName, new ClientJsonString(propertyValueString)); } } } private static int upgradeAccelerometerSensorProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The AccelerometerSensor.MinimumInterval property was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeActivityStarterProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The ActivityStarter.DataType, ActivityStarter.ResultType, and ActivityStarter.ResultUri // properties were added. // The ActivityStarter.ResolveActivity method was added. // The ActivityStarter.ActivityError event was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The ActivityStarter.ActivityError event was marked userVisible false and is no longer // used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } return srcCompVersion; } private static int upgradeBallProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Heading property was changed from int to double srcCompVersion = 2; } if (srcCompVersion < 3) { // The Z property was added srcCompVersion = 3; } if (srcCompVersion < 4) { // The TouchUp, TouchDown, and Flung events were added. (for all sprites) // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeBluetoothClientProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The BluetoothClient.Enabled property was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The BluetoothClient.BluetoothError event was marked userVisible false and is no longer // used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The BluetoothClient.DelimiterByte property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } if (srcCompVersion < 5) { // The BluetoothClient.Secure property was added. // No properties need to be modified to upgrade to version 5. srcCompVersion = 5; } return srcCompVersion; } private static int upgradeBluetoothServerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The BluetoothServer.Enabled property was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The BluetoothServer.BluetoothError event was marked userVisible false and is no longer // used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The BluetoothServer.DelimiterByte property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } if (srcCompVersion < 5) { // The BluetoothServer.Secure property was added. // No properties need to be modified to upgrade to version 5. srcCompVersion = 5; } return srcCompVersion; } private static int upgradeButtonProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The LongClick event was added. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Shape property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeCanvasProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The LineWidth property was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The FontSize and TextAlignment properties and // the DrawText and DrawTextAtAngle methods were added. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // No properties need to be modified to upgrade to version 4. // The Save and SaveAs methods were added. srcCompVersion = 4; } if (srcCompVersion < 5) { // No properties need to be modified to upgrade to version 5. // The methods GetBackgroundPixelColor, GetPixelColor, and // SetBackgroundPixelColor were added. srcCompVersion = 5; } if (srcCompVersion < 6) { // No properties need to be modified to upgrade to version 6. // The events TouchDown, TouchUp, and Flung were added. srcCompVersion = 6; } return srcCompVersion; } private static int upgradeCheckBoxProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Value property was renamed to Checked. handlePropertyRename(componentProperties, "Value", "Checked"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeContactPickerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Open method was added. No changes are needed. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Shape property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeEmailPickerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeFormProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Screen.Scrollable property was added. // If the form contains a direct child component whose height is set to fill parent, // we set the Scrollable property value to false. if (componentProperties.containsKey("$Components")) { JSONArray componentsArray = componentProperties.get("$Components").asArray(); for (JSONValue nestedComponent : componentsArray.getElements()) { Map<String, JSONValue> nestedComponentProperties = nestedComponent.asObject().getProperties(); if (nestedComponentProperties.containsKey("Height")) { JSONValue heightValue = nestedComponentProperties.get("Height"); String heightString = heightValue.asString().getString(); try { int height = Integer.parseInt(heightString); if (height == MockVisibleComponent.LENGTH_FILL_PARENT) { // Set the Form's Scrollable property to false. componentProperties.put("Scrollable", new ClientJsonString("False")); break; } } catch (NumberFormatException e) { // Ignore this. If we throw an exception here, the project is unrecoverable. } } } } // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Screen.Icon property was added. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Screen.ErrorOccurred event was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } if (srcCompVersion < 5) { // The Screen.ScreenOrientation property and Screen.ScreenOrientationChanged event were // added. // No properties need to be modified to upgrade to version 5. srcCompVersion = 5; } if (srcCompVersion < 6) { // The SwitchForm and SwitchFormWithArgs methods were removed and the OtherScreenClosed event // was added. srcCompVersion = 6; } if (srcCompVersion < 7) { // The VersionCode and VersionName properties were added. No properties need to be modified // to update to version 7. srcCompVersion = 7; } if (srcCompVersion < 8) { // The AlignHorizontal and AlignVertical properties were added. No blocks need to be modified // to upgrade to version 8. srcCompVersion = 8; } return srcCompVersion; } private static int upgradeHorizontalArrangementProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The AlignHorizontal and AlignVertical properties were added. No blocks need to be modified // to upgrqde to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeImagePickerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Open method was added. No changes are needed. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Shape property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeImageSpriteProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The SpriteComponent.Rotates property was added // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Heading property was changed from int to Double srcCompVersion = 3; } if (srcCompVersion < 4) { // The Z property was added srcCompVersion = 4; } if (srcCompVersion < 5) { // The TouchUp, TouchDown, and Flung events were added. (for all sprites) // No properties need to be modified to upgrade to version 5. srcCompVersion = 5; } return srcCompVersion; } private static int upgradeLabelProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeListPickerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The SelectionIndex property was added. No changes are needed. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Open method was added. No changes are needed. srcCompVersion = 4; } if (srcCompVersion < 5) { // The Shape property was added. // No properties need to be modified to upgrade to version 5. srcCompVersion = 5; } return srcCompVersion; } private static int upgradeLocationSensorProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The TimeInterval and DistanceInterval properties were added. // No properties need to be modified to upgrade to Version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeOrientationSensorProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Yaw property was renamed to Azimuth. handlePropertyRename(componentProperties, "Yaw", "Azimuth"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradePasswordTextBoxProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradePhoneNumberPickerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Open method was added. No changes are needed. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Shape property was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradePlayerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Player.PlayerError event was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Player.PlayerError event was marked userVisible false and is no longer used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The Looping and Volume properties were added. // The Completed Event was added. // The IsPlaying method was added. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeSoundProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The Sound.SoundError event was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Sound.SoundError event was marked userVisible false and is no longer used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } return srcCompVersion; } private static int upgradeTinyWebDBProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The TinyWebDB.ShowAlert method was removed. Notifier.ShowAlert should be used instead. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeVerticalArrangementProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The AlignHorizontal and AlignVertical properties were added. No blocks need to be modified // to upgrqde to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeVideoPlayerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The VideoPlayer.VideoPlayerError event was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The VideoPlayer.VideoPlayerError event was marked userVisible false and is no longer used. // No properties need to be modified to upgrade to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The VideoPlayer.height and VideoPlayer.width getter and setters were marked as // visible to the user. // The FullScreen property was created. // No properties need to be modified to upgrade to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeTwitterProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The designer properties ConsumerKey and ConsumerSecret were added // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeTextBoxProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The property (and designer property) TextBox.NumbersOnly was added // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } if (srcCompVersion < 3) { // The Alignment property was renamed to TextAlignment. handlePropertyRename(componentProperties, "Alignment", "TextAlignment"); // Properties related to this component have now been upgraded to version 3. srcCompVersion = 3; } if (srcCompVersion < 4) { // The MultiLine property was added. // The default for Multiline from now on is false, but up until now, // all text boxes have been multiline. // We need to set the MultiLine to true when we upgrade old projects. componentProperties.put("MultiLine", new ClientJsonString("True")); // Properties related to this component have now been upgraded to version 4. srcCompVersion = 4; } return srcCompVersion; } private static int upgradeWebProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The RequestHeaders and AllowCookies properties were added. // The BuildPostData and ClearCookies methods were added. // The existing PostText method was renamed to PostTextWithEncoding, and a new PostText // method was added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } return srcCompVersion; } private static int upgradeWebViewerProperties( Map<String, JSONValue> componentProperties, int srcCompVersion) { if (srcCompVersion < 2) { // The CanGoForward and CanGoBack methods were added. // No properties need to be modified to upgrade to version 2. srcCompVersion = 2; } return srcCompVersion; } private static void handlePropertyRename( Map<String, JSONValue> componentProperties, String oldPropName, String newPropName) { if (componentProperties.containsKey(oldPropName)) { componentProperties.put(newPropName, componentProperties.remove(oldPropName)); } } }