Permalink
Browse files

Adds support for building apps on API Level 4

   - Increases minSDK to 4 in Compiler.java (takes App Inventor out of
     screen compatibility mode).
   - Adds a checkbox to preview the app on tablet or phone size.
   - Adds correction for resolution of images depending on density of
     device
   - changes pixels for DPs
   - Adds scaleToFit property to Image component

Change-Id: I2938a2254f59eb534785afd37ec8891d9148e190
  • Loading branch information...
1 parent 721f413 commit 369188912ec963e4dbae7248d170f0ed362f8720 @josmas josmas committed with jisqyv Apr 16, 2014
Showing with 262 additions and 70 deletions.
  1. +6 −0 appinventor/appengine/src/com/google/appinventor/client/Images.java
  2. +14 −6 appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
  3. +39 −0 ...entor/appengine/src/com/google/appinventor/client/editor/simple/SimpleVisibleComponentsPanel.java
  4. +98 −27 appinventor/appengine/src/com/google/appinventor/client/editor/simple/components/MockForm.java
  5. +32 −2 appinventor/appengine/src/com/google/appinventor/client/editor/simple/components/MockImageBase.java
  6. +4 −4 ...om/google/appinventor/client/editor/youngandroid/properties/YoungAndroidLengthPropertyEditor.java
  7. +3 −0 appinventor/appengine/src/com/google/appinventor/client/settings/project/YoungAndroidSettings.java
  8. BIN appinventor/appengine/src/com/google/appinventor/images/navigationbar.png
  9. +1 −0 appinventor/appengine/src/com/google/appinventor/shared/settings/SettingsConstants.java
  10. +6 −1 appinventor/appengine/war/Ya.css
  11. +3 −17 appinventor/buildserver/src/com/google/appinventor/buildserver/Compiler.java
  12. +5 −2 appinventor/components/src/com/google/appinventor/components/runtime/Ball.java
  13. +5 −4 appinventor/components/src/com/google/appinventor/components/runtime/Canvas.java
  14. +7 −0 appinventor/components/src/com/google/appinventor/components/runtime/Image.java
  15. +19 −1 appinventor/components/src/com/google/appinventor/components/runtime/util/MediaUtil.java
  16. +20 −6 appinventor/components/src/com/google/appinventor/components/runtime/util/ViewUtil.java
@@ -68,6 +68,12 @@
ImageResource phonebar();
/**
+ * Navigation bar shown below the form in the visual designer
+ */
+ @Source("com/google/appinventor/images/navigationbar.png")
+ ImageResource navigationbar();
+
+ /**
* Designer palette item: question mark for more component information
*/
@Source("com/google/appinventor/images/help.png")
@@ -397,6 +397,14 @@
@Description("Checkbox controlling whether to display invisible components in the designer.")
String showHiddenComponentsCheckbox();
+ @DefaultMessage("Check to see Preview on Tablet size.")
+ @Description("Checkbox (check) controlling whether to display a preview on Tablet size.")
+ String previewTabletSize();
+
+ @DefaultMessage("Un-check to see Preview on Phone size.")
+ @Description("Checkbox (un-check) controlling whether to display a preview on Phone size.")
+ String previewPhoneSize();
+
// Used in editor/simple/components/MockComponent.java
@DefaultMessage("Rename Component")
@@ -670,13 +678,13 @@
@Description("Caption and summary for Fill Parent choice")
String fillParentCaption();
- @DefaultMessage("pixels")
- @Description("Caption for pixels label")
- String pixelsCaption();
+ @DefaultMessage("DP") // DP - Density Independent Pixels
+ @Description("Caption for DPs label")
+ String dpsCaption();
- @DefaultMessage("{0} pixels")
- @Description("Summary for custom length in pixels")
- String pixelsSummary(String pixels);
+ @DefaultMessage("{0} DPs")
+ @Description("Summary for custom length in DPs")
+ String dpsSummary(String dps);
@DefaultMessage("The value must be a number greater than or equal to 0")
@Description("Error shown after validation of custom length field failed.")
@@ -27,6 +27,7 @@
// UI elements
private final VerticalPanel phoneScreen;
private final CheckBox checkboxShowHiddenComponents;
+ private final CheckBox checkboxPhoneTablet; // A CheckBox for Phone/Tablet preview sizes
// Corresponding panel for non-visible components (because we allow users to drop
// non-visible components onto the form, but we show them in the non-visible
@@ -77,9 +78,47 @@ public void onValueChange(ValueChangeEvent<Boolean> event) {
});
phoneScreen.add(checkboxShowHiddenComponents);
+ checkboxPhoneTablet = new CheckBox(MESSAGES.previewPhoneSize()) {
+ @Override
+ protected void onLoad() {
+ // onLoad is called immediately after a widget becomes attached to the browser's document.
+ boolean showPhoneTablet = Boolean.parseBoolean(
+ projectEditor.getProjectSettingsProperty(
+ SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
+ SettingsConstants.YOUNG_ANDROID_SETTINGS_PHONE_TABLET));
+ checkboxPhoneTablet.setValue(showPhoneTablet);
+ changeFormPreviewSize(showPhoneTablet);
+ }
+ };
+ checkboxPhoneTablet.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<Boolean> event) {
+ boolean isChecked = event.getValue(); // auto-unbox from Boolean to boolean
+ projectEditor.changeProjectSettingsProperty(
+ SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
+ SettingsConstants.YOUNG_ANDROID_SETTINGS_PHONE_TABLET,
+ isChecked ? "True" : "False");
+ changeFormPreviewSize(isChecked);
+ }
+ });
+ phoneScreen.add(checkboxPhoneTablet);
+
initWidget(phoneScreen);
}
+ private void changeFormPreviewSize(boolean isChecked) {
+ if (form != null){
+ if (isChecked){
+ form.changePreviewSize(true);
+ checkboxPhoneTablet.setText(MESSAGES.previewPhoneSize());
+ }
+ else {
+ form.changePreviewSize(false);
+ checkboxPhoneTablet.setText(MESSAGES.previewTabletSize());
+ }
+ }
+ }
+
/**
* Associates a Simple form component with this panel.
*
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
-// Copyright 2011-2012 MIT, All rights reserved
+// Copyright 2011-2014 MIT, All rights reserved
// Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt
package com.google.appinventor.client.editor.simple.components;
@@ -32,7 +32,11 @@
import com.google.gwt.user.client.ui.TreeItem;
/**
- * Mock Form component.
+ * Mock Form component. This implementation provides two main preview sizes corresponding to the
+ * 'normal' and 'large' buckets (http://developer.android.com/guide/practices/screens_support
+ * .html).
+ * Normal size is a 1:1 with pixels on a device with dpi:160. We use that as the baseline for the
+ * browser too. All UI elements should be scaled to DP for buckets other than 'normal'.
*
*/
public final class MockForm extends MockContainer {
@@ -41,9 +45,7 @@
* Widget for the mock form title bar.
*/
private class TitleBar extends Composite {
- private static final int HEIGHT = 24;
-
-
+ private static final int HEIGHT = 22;
// UI elements
private Label title;
@@ -101,19 +103,62 @@ void changeTitle(String newTitle) {
}
}
+ /*
+ * Widget for a mock phone navigation bar; Shows at the bottom of the viewer
+ */
+ private class NavigationBar extends Composite {
+ private static final int HEIGHT = 35;
+
+ // UI elements
+ private DockPanel bar;
+ private Image navigationBarImage;
+
+ /*
+ * Creates a new phone navigation bar; Shows at the bottom of the viewer.
+ */
+ NavigationBar() {
+ navigationBarImage = new Image(images.navigationbar());
+
+ bar = new DockPanel();
+ bar.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
+ bar.add(navigationBarImage, DockPanel.CENTER);
+
+ initWidget(bar);
+
+ setStylePrimaryName("ode-SimpleMockFormPhoneBar"); //reuse the css for the phone
+ setSize("100%", HEIGHT + "px");
+ }
+ }
+
/**
* Component type name.
*/
public static final String TYPE = "Form";
private static final String VISIBLE_TYPE = "Screen";
- // TODO(lizlooney) 320x480 is the resolution of the G1. Do we want to change this to the
- // resolution of the Nexus One?
- private static final int PORTRAIT_WIDTH = 320;
- private static final int PORTRAIT_HEIGHT = 480;
- private static final int LANDSCAPE_WIDTH = 480;
- private static final int LANDSCAPE_HEIGHT = 320;
+ // Currently App Inventor provides two main sizes that correspond to 'normal' and 'large'
+ // screens. We use phone=normal and tablet=large.
+ // More information about 'bucket' sizes at:
+ // http://developer.android.com/guide/practices/screens_support.html
+ // The values for Phone and Tablet were decided by trial and error. The main reason is that in
+ // the designer we use sizes of GWT widgets, and not the sizes of the actual Android widgets.
+ private static final int PHONE_PORTRAIT_WIDTH = 380;
+ private static final int PHONE_PORTRAIT_HEIGHT = 524;
+ private static final int PHONE_LANDSCAPE_WIDTH = PHONE_PORTRAIT_HEIGHT;
+ private static final int PHONE_LANDSCAPE_HEIGHT = PHONE_PORTRAIT_WIDTH;
+
+ private static final int TABLET_PORTRAIT_WIDTH = 600;
+ private static final int TABLET_PORTRAIT_HEIGHT = 924;
+ private static final int TABLET_LANDSCAPE_WIDTH = TABLET_PORTRAIT_HEIGHT;
+ private static final int TABLET_LANDSCAPE_HEIGHT = TABLET_PORTRAIT_WIDTH;
+
+ // These are default values but they can be changed in the changePreviewSize method
+ private int PORTRAIT_WIDTH = PHONE_PORTRAIT_WIDTH;
+ private int PORTRAIT_HEIGHT = PHONE_PORTRAIT_HEIGHT;
+ private int LANDSCAPE_WIDTH = PHONE_LANDSCAPE_WIDTH;
+ private int LANDSCAPE_HEIGHT = PHONE_LANDSCAPE_HEIGHT;
+ private boolean landscape = false;
// Property names
private static final String PROPERTY_NAME_TITLE = "Title";
@@ -179,13 +224,8 @@ public MockForm(SimpleEditor editor) {
scrollPanel = new ScrollPanel(rootPanel);
formWidget.add(scrollPanel);
- screenWidth = PORTRAIT_WIDTH;
- screenHeight = PORTRAIT_HEIGHT;
- usableScreenHeight = screenHeight - PhoneBar.HEIGHT - TitleBar.HEIGHT;
-
- // This is just the initial size of the form. It will be resized in refresh();
- rootPanel.setPixelSize(screenWidth, usableScreenHeight);
- resizePanels();
+ //Add navigation bar at the bottom of the viewer.
+ formWidget.add(new NavigationBar());
initComponent(formWidget);
@@ -196,21 +236,50 @@ public MockForm(SimpleEditor editor) {
} catch (BadPropertyEditorException e) {
OdeLog.log(MESSAGES.badAlignmentPropertyEditorForArrangement());
return;
- };
+ }
enableAndDisableDropdowns();
initialized = true;
}
+ public void changePreviewSize(boolean isTablet) {
+ if (isTablet) {
+ PORTRAIT_WIDTH = TABLET_PORTRAIT_WIDTH;
+ PORTRAIT_HEIGHT = TABLET_PORTRAIT_HEIGHT;
+ LANDSCAPE_WIDTH = TABLET_LANDSCAPE_WIDTH;
+ LANDSCAPE_HEIGHT = TABLET_LANDSCAPE_HEIGHT;
+ }
+ else {
+ PORTRAIT_WIDTH = PHONE_PORTRAIT_WIDTH;
+ PORTRAIT_HEIGHT = PHONE_PORTRAIT_HEIGHT;
+ LANDSCAPE_WIDTH = PHONE_LANDSCAPE_WIDTH;
+ LANDSCAPE_HEIGHT = PHONE_LANDSCAPE_HEIGHT;
+ }
+ if (landscape)
+ resizePanel(LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT);
+ else
+ resizePanel(PORTRAIT_WIDTH, PORTRAIT_HEIGHT);
+ }
+
/*
* Resizes the scrollPanel and formWidget based on the screen size.
*/
- private void resizePanels() {
- // Set the scrollPanel's width to account for the width of the vertical scrollbar.
- int vertScrollbarWidth = getVerticalScrollbarWidth();
- scrollPanel.setPixelSize(screenWidth + vertScrollbarWidth, usableScreenHeight);
- formWidget.setPixelSize(screenWidth + vertScrollbarWidth, screenHeight);
- }
+ public void resizePanel(int newWidth, int newHeight){
+ screenWidth = newWidth;
+ screenHeight = newHeight;
+ usableScreenHeight = screenHeight - PhoneBar.HEIGHT - TitleBar.HEIGHT - NavigationBar.HEIGHT;
+ rootPanel.setPixelSize(screenWidth, usableScreenHeight);
+ scrollPanel.setPixelSize(screenWidth + getVerticalScrollbarWidth(), usableScreenHeight);
+ formWidget.setPixelSize(screenWidth + getVerticalScrollbarWidth(), screenHeight);
+
+ // Store properties
+ changeProperty(PROPERTY_NAME_WIDTH, "" + screenWidth);
+ boolean scrollable = Boolean.parseBoolean(getPropertyValue(PROPERTY_NAME_SCROLLABLE));
+ if (!scrollable) {
+ changeProperty(PROPERTY_NAME_HEIGHT, "" + usableScreenHeight);
+ }
+
+ }
/*
* Returns the width of a vertical scroll bar, calculating it if necessary.
*/
@@ -366,12 +435,14 @@ private void setScreenOrientationProperty(String text) {
if (text.equalsIgnoreCase("landscape")) {
screenWidth = LANDSCAPE_WIDTH;
screenHeight = LANDSCAPE_HEIGHT;
+ landscape = true;
} else {
screenWidth = PORTRAIT_WIDTH;
screenHeight = PORTRAIT_HEIGHT;
+ landscape = false;
}
- usableScreenHeight = screenHeight - PhoneBar.HEIGHT - TitleBar.HEIGHT;
- resizePanels();
+ usableScreenHeight = screenHeight - PhoneBar.HEIGHT - TitleBar.HEIGHT - NavigationBar.HEIGHT;
+ resizePanel(screenWidth, screenHeight);
changeProperty(PROPERTY_NAME_WIDTH, "" + screenWidth);
boolean scrollable = Boolean.parseBoolean(getPropertyValue(PROPERTY_NAME_SCROLLABLE));
@@ -13,6 +13,8 @@
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
@@ -24,10 +26,12 @@
abstract class MockImageBase extends MockVisibleComponent {
// Property names
private static final String PROPERTY_NAME_PICTURE = "Picture";
+ private static final String PROPERTY_SCALE_PICTURE_TO_FIT = "ScalePictureToFit";
// Widget for showing the image.
private final Image image;
private String picturePropValue;
+ private boolean fitToScale;
MockImageBase(SimpleEditor editor, String type, ImageResource icon) {
super(editor, type, icon);
@@ -46,13 +50,12 @@ public void onError(ErrorEvent event) {
image.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
- // Resize to outer container, fixes issue with setting precise size in designer
- image.setSize("100%", "100%");
refreshForm();
}
});
SimplePanel simplePanel = new SimplePanel();
simplePanel.setStylePrimaryName("ode-SimpleMockComponent");
+ simplePanel.addStyleName("imageComponentCenterPanel");
simplePanel.setWidget(image);
initComponent(simplePanel);
}
@@ -105,5 +108,32 @@ public void onPropertyChange(String propertyName, String newValue) {
setPictureProperty(newValue);
refreshForm();
}
+ else if (propertyName.equals(PROPERTY_SCALE_PICTURE_TO_FIT)) {
+ setScalingProperty(newValue);
+ refreshForm();
+ }
+ else if (propertyName.equals(PROPERTY_NAME_WIDTH)) {
+ image.setWidth(newValue + "px");
+ refreshForm();
+ }
+ else if (propertyName.equals(PROPERTY_NAME_HEIGHT)) {
+ image.setHeight(newValue + "px");
+ refreshForm();
+ }
+ }
+
+ /**
+ * property to make the picture scale to fit its parents width and height
+ * @param newValue true will scale the picture
+ */
+ private void setScalingProperty(String newValue) {
+ if (newValue.equals("True")){
+ fitToScale = true;
+ image.setSize("100%", "100%");
+ }
+ else {
+ fitToScale = false;
+ image.setSize(getPreferredWidth() + "px", getPreferredHeight() + "px");
+ }
}
}
@@ -52,9 +52,9 @@ public YoungAndroidLengthPropertyEditor() {
Panel customRow = new HorizontalPanel();
customRow.add(customLengthRadioButton);
customRow.add(customLengthField);
- Label pixels = new Label(MESSAGES.pixelsCaption());
- pixels.setStylePrimaryName("ode-PixelsLabel");
- customRow.add(pixels);
+ Label dps = new Label(MESSAGES.dpsCaption());
+ dps.setStylePrimaryName("ode-DPsLabel");
+ customRow.add(dps);
Panel panel = new VerticalPanel();
panel.add(automaticRadioButton);
@@ -112,7 +112,7 @@ protected String getPropertyValueSummary() {
} else if (lengthHint.equals(CONST_FILL_PARENT)) {
return MESSAGES.fillParentCaption();
} else {
- return MESSAGES.pixelsSummary(lengthHint);
+ return MESSAGES.dpsSummary(lengthHint);
}
}
@@ -30,6 +30,9 @@ public YoungAndroidSettings(Project project) {
SettingsConstants.YOUNG_ANDROID_SETTINGS_SHOW_HIDDEN_COMPONENTS,
"False", EditableProperty.TYPE_INVISIBLE));
addProperty(new EditableProperty(this,
+ SettingsConstants.YOUNG_ANDROID_SETTINGS_PHONE_TABLET,
+ "False", EditableProperty.TYPE_INVISIBLE));
+ addProperty(new EditableProperty(this,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE, "1",
EditableProperty.TYPE_INVISIBLE));
addProperty(new EditableProperty(this,
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.

3 comments on commit 3691889

@themadrobot
Contributor

Great addition to AI

@josmas
Member
josmas commented on 3691889 Jul 25, 2014

Thanks @kkashi! If you give it a go, let us know if anything is broken! :)

Please sign in to comment.