Commit 135acbca authored by Andrew F. McKinney's avatar Andrew F. McKinney

Merge pull request #51 from klaverty/version-number

Version number for submitting app to Google Play.
parents a1203f6b 0187dd51
......@@ -109,6 +109,8 @@ public final class MockForm extends MockContainer {
private static final String PROPERTY_NAME_SCREEN_ORIENTATION = "ScreenOrientation";
private static final String PROPERTY_NAME_SCROLLABLE = "Scrollable";
private static final String PROPERTY_NAME_ICON = "Icon";
private static final String PROPERTY_NAME_VCODE = "VersionCode";
private static final String PROPERTY_NAME_VNAME = "VersionName";
// Form UI components
AbsolutePanel formWidget;
......@@ -278,6 +280,16 @@ public final class MockForm extends MockContainer {
// The Icon property actually applies to the application and is only visible on Screen1.
return editor.isScreen1();
}
if (propertyName.equals(PROPERTY_NAME_VNAME)) {
// The VersionName property actually applies to the application and is only visible on Screen1.
return editor.isScreen1();
}
if (propertyName.equals(PROPERTY_NAME_VCODE)) {
// The VersionCode property actually applies to the application and is only visible on Screen1.
return editor.isScreen1();
}
return super.isPropertyVisible(propertyName);
}
......@@ -345,6 +357,28 @@ public final class MockForm extends MockContainer {
SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON, icon);
}
}
private void setVCodeProperty(String vcode) {
// The VersionCode property actually applies to the application and is only visible on Screen1.
// When we load a form that is not Screen1, this method will be called with the default value
// for VersionCode (1). We need to ignore that.
if (editor.isScreen1()) {
editor.getProjectEditor().changeProjectSettingsProperty(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE, vcode);
}
}
private void setVNameProperty(String vname) {
// The VersionName property actually applies to the application and is only visible on Screen1.
// When we load a form that is not Screen1, this method will be called with the default value
// for VersionName (1.0). We need to ignore that.
if (editor.isScreen1()) {
editor.getProjectEditor().changeProjectSettingsProperty(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME, vname);
}
}
/**
* Forces a re-layout of the child components of the container.
......@@ -534,6 +568,10 @@ public final class MockForm extends MockContainer {
titleBar.changeTitle(newValue);
} else if (propertyName.equals(PROPERTY_NAME_ICON)) {
setIconProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_VCODE)) {
setVCodeProperty(newValue);
} else if (propertyName.equals(PROPERTY_NAME_VNAME)) {
setVNameProperty(newValue);
}
}
}
......@@ -26,5 +26,11 @@ public final class YoungAndroidSettings extends Settings {
addProperty(new EditableProperty(this,
SettingsConstants.YOUNG_ANDROID_SETTINGS_SHOW_HIDDEN_COMPONENTS,
"False", EditableProperty.TYPE_INVISIBLE));
addProperty(new EditableProperty(this,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE, "1",
EditableProperty.TYPE_INVISIBLE));
addProperty(new EditableProperty(this,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME, "1.0",
EditableProperty.TYPE_INVISIBLE));
}
}
......@@ -561,6 +561,11 @@ public final class YoungAndroidFormUpgrader {
// 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;
}
return srcCompVersion;
}
......
......@@ -93,7 +93,7 @@ public final class FileImporterImpl implements FileImporter {
// so that it contains the correct entries for "main" and "name", which are dependent on
// the projectName and qualifiedFormName.
String content = YoungAndroidProjectService.getProjectPropertiesFileContents(
projectName, qualifiedFormName, null);
projectName, qualifiedFormName, null, null, null);
project.addTextFile(new TextFile(fileName, content));
isProjectArchive = true;
......@@ -146,7 +146,7 @@ public final class FileImporterImpl implements FileImporter {
if (projectHistory != null) {
project.setProjectHistory(projectHistory);
}
String settings = YoungAndroidProjectService.getProjectSettings(null);
String settings = YoungAndroidProjectService.getProjectSettings(null, null, null);
long projectId = storageIo.createProject(userId, project, settings);
return new UserProject(projectId, storageIo.getProjectName(userId, projectId),
storageIo.getProjectType(userId, projectId),
......
......@@ -115,11 +115,15 @@ public final class YoungAndroidProjectService extends CommonProjectService {
/**
* Returns project settings that can be used when creating a new project.
*/
public static String getProjectSettings(String icon) {
public static String getProjectSettings(String icon, String vCode, String vName) {
icon = Strings.nullToEmpty(icon);
vCode = Strings.nullToEmpty(vCode);
vName = Strings.nullToEmpty(vName);
return "{\"" + SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS + "\":{" +
"\"" + SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON + "\":\"" +
icon + "\"}}";
icon + "\",\"" + SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE +
"\":\"" + vCode +"\",\"" + SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME +
"\":\"" + vName + "\"}}";
}
/**
......@@ -129,9 +133,11 @@ public final class YoungAndroidProjectService extends CommonProjectService {
* @param projectName the name of the project
* @param qualifiedName the qualified name of Screen1 in the project
* @param icon the name of the asset to use as the application icon
* @param vcode the version code
* @param vname the version name
*/
public static String getProjectPropertiesFileContents(String projectName, String qualifiedName,
String icon) {
String icon, String vcode, String vname) {
String contents = "main=" + qualifiedName + "\n" +
"name=" + projectName + '\n' +
"assets=../" + ASSETS_FOLDER + "\n" +
......@@ -140,6 +146,12 @@ public final class YoungAndroidProjectService extends CommonProjectService {
if (icon != null && !icon.isEmpty()) {
contents += "icon=" + icon + "\n";
}
if (vcode != null && !vcode.isEmpty()) {
contents += "versioncode=" + vcode + "\n";
}
if (vname != null && !vname.isEmpty()) {
contents += "versionname=" + vname + "\n";
}
return contents;
}
......@@ -203,6 +215,12 @@ public final class YoungAndroidProjectService extends CommonProjectService {
String newIcon = Strings.nullToEmpty(settings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON));
String newVCode = Strings.nullToEmpty(settings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE));
String newVName = Strings.nullToEmpty(settings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME));
// Extract the old icon from the project.properties file from storageIo.
String projectProperties = storageIo.downloadFile(userId, projectId,
......@@ -216,12 +234,14 @@ public final class YoungAndroidProjectService extends CommonProjectService {
return;
}
String oldIcon = Strings.nullToEmpty(properties.getProperty("icon"));
String oldVCode = Strings.nullToEmpty(properties.getProperty("versioncode"));
String oldVName = Strings.nullToEmpty(properties.getProperty("versionname"));
if (!newIcon.equals(oldIcon)) {
if (!newIcon.equals(oldIcon) || !newVCode.equals(oldVCode) || !newVName.equals(oldVName)) {
// Recreate the project.properties and upload it to storageIo.
String projectName = properties.getProperty("name");
String qualifiedName = properties.getProperty("main");
String newContent = getProjectPropertiesFileContents(projectName, qualifiedName, newIcon);
String newContent = getProjectPropertiesFileContents(projectName, qualifiedName, newIcon, newVCode, newVName);
storageIo.uploadFile(projectId, PROJECT_PROPERTIES_FILE_NAME, userId,
newContent, StorageUtil.DEFAULT_CHARSET);
}
......@@ -240,7 +260,7 @@ public final class YoungAndroidProjectService extends CommonProjectService {
String propertiesFileName = PROJECT_PROPERTIES_FILE_NAME;
String propertiesFileContents = getProjectPropertiesFileContents(projectName,
qualifiedFormName, null);
qualifiedFormName, null, null, null);
String formFileName = getFormPropertiesFileName(qualifiedFormName);
String formFileContents = getInitialFormPropertiesFileContents(qualifiedFormName);
......@@ -256,7 +276,7 @@ public final class YoungAndroidProjectService extends CommonProjectService {
project.addTextFile(new TextFile(codeblocksFileName, codeblocksFileContents));
// Create new project
return storageIo.createProject(userId, project, getProjectSettings(""));
return storageIo.createProject(userId, project, getProjectSettings("", "1", "1.0"));
}
@Override
......@@ -268,6 +288,12 @@ public final class YoungAndroidProjectService extends CommonProjectService {
String icon = oldSettings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON);
String vcode = oldSettings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE);
String vname = oldSettings.getSetting(
SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS,
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME);
Project newProject = new Project(newName);
newProject.setProjectType(YoungAndroidProjectNode.YOUNG_ANDROID_PROJECT_TYPE);
......@@ -286,7 +312,7 @@ public final class YoungAndroidProjectService extends CommonProjectService {
// name and qualified name.
String qualifiedFormName = StringUtils.getQualifiedFormName(
storageIo.getUser(userId).getUserEmail(), newName);
newContents = getProjectPropertiesFileContents(newName, qualifiedFormName, icon);
newContents = getProjectPropertiesFileContents(newName, qualifiedFormName, icon, vcode, vname);
} else {
// This is some file other than the project properties file.
// oldSourceFileName may contain the old project name as a path segment, surrounded by /.
......@@ -309,7 +335,7 @@ public final class YoungAndroidProjectService extends CommonProjectService {
}
// Create the new project and return the new project's id.
return storageIo.createProject(userId, newProject, getProjectSettings(icon));
return storageIo.createProject(userId, newProject, getProjectSettings(icon, vcode, vname));
}
@Override
......
......@@ -27,4 +27,6 @@ public class SettingsConstants {
// Project settings
public static final String YOUNG_ANDROID_SETTINGS_ICON = "Icon";
public static final String YOUNG_ANDROID_SETTINGS_SHOW_HIDDEN_COMPONENTS = "ShowHiddenComponents";
public static final String YOUNG_ANDROID_SETTINGS_VERSION_CODE = "VersionCode";
public static final String YOUNG_ANDROID_SETTINGS_VERSION_NAME = "VersionName";
}
......@@ -320,7 +320,9 @@ public class ProjectServiceTest {
"name=Project2\n" +
"assets=../assets\n" +
"source=../src\n" +
"build=../build\n");
"build=../build\n" +
"versioncode=1\n" +
"versionname=1.0\n");
expectedYaFiles2.put("src/com/domain/noname/Project2/Screen1.scm",
YOUNG_ANDROID_PROJECT_SCM_SOURCE);
assertEquals(expectedYaFiles2, getTextFiles(USER_ID_ONE, yaProject2));
......@@ -458,7 +460,9 @@ public class ProjectServiceTest {
String loadedSettings = projectServiceImpl.loadProjectSettings(projectId);
assertEquals(
"{\"" + SettingsConstants.PROJECT_YOUNG_ANDROID_SETTINGS + "\":" +
"{\"" + SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON + "\":\"\"}}",
"{\"" + SettingsConstants.YOUNG_ANDROID_SETTINGS_ICON + "\":\"\",\"" +
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_CODE + "\":\"1\",\"" +
SettingsConstants.YOUNG_ANDROID_SETTINGS_VERSION_NAME + "\":\"1.0\"}}",
loadedSettings);
String storedSettings =
......
......@@ -719,6 +719,11 @@ public class BlockSaveFile {
// was added.
blkCompVersion = 6;
}
if (blkCompVersion < 7) {
// The VersionCode and VersionName properties were added. No blocks need to be modified
// to update to version 7.
blkCompVersion = 7;
}
return blkCompVersion;
}
......
......@@ -58,6 +58,9 @@ public final class Compiler {
private static final String DEFAULT_ICON =
RUNTIME_FILES_DIR + "ya.png";
private static final String DEFAULT_VERSION_CODE = "1";
private static final String DEFAULT_VERSION_NAME = "1.0";
private static final String COMPONENT_PERMISSIONS =
RUNTIME_FILES_DIR + "simple_components_permissions.json";
......@@ -158,7 +161,11 @@ public final class Compiler {
String packageName = Signatures.getPackageName(mainClass);
String className = Signatures.getClassName(mainClass);
String projectName = project.getProjectName();
String vCode = (project.getVCode() == null) ? DEFAULT_VERSION_CODE : project.getVCode();
String vName = (project.getVName() == null) ? DEFAULT_VERSION_NAME : project.getVName();
LOG.log(Level.INFO, "VCode: " + project.getVCode());
LOG.log(Level.INFO, "VName: " + project.getVName());
// TODO(user): Use com.google.common.xml.XmlWriter
try {
BufferedWriter out = new BufferedWriter(new FileWriter(manifestFile));
......@@ -171,7 +178,7 @@ public final class Compiler {
"package=\"" + packageName + "\" " +
// TODO(markf): uncomment the following line when we're ready to enable publishing to the
// Android Market.
"android:versionCode=\"3\" " + "android:versionName=\"1.5\" " +
"android:versionCode=\"" + vCode +"\" " + "android:versionName=\"" + vName + "\" " +
">\n");
for (String permission : permissionsNeeded) {
out.write(" <uses-permission android:name=\"" + permission + "\" />\n");
......
......@@ -61,6 +61,8 @@ public final class Project {
* main - qualified name of main form class
* name - application name
* icon - application icon
* versioncode - version code
* versionname - version name
* source - comma separated list of source root directories
* assets - assets directory (for image and data files bundled with the application)
* build - output directory for the compiler
......@@ -69,6 +71,8 @@ public final class Project {
private static final String NAMETAG = "name";
private static final String ICONTAG = "icon";
private static final String SOURCETAG = "source";
private static final String VCODETAG = "versioncode";
private static final String VNAMETAG = "versionname";
private static final String ASSETSTAG = "assets";
private static final String BUILDTAG = "build";
......@@ -183,6 +187,42 @@ public final class Project {
public void setIcon(String icon) {
properties.setProperty(ICONTAG, icon);
}
/**
* Returns the version code.
*
* @return version code
*/
public String getVCode() {
return properties.getProperty(VCODETAG);
}
/**
* Sets the version code.
*
* @param vcode version code
*/
public void setVCode(String vcode) {
properties.setProperty(VCODETAG, vcode);
}
/**
* Returns the version name.
*
* @return version name
*/
public String getVName() {
return properties.getProperty(VNAMETAG);
}
/**
* Sets the version name.
*
* @param vname version name
*/
public void setVName(String vname) {
properties.setProperty(VNAMETAG, vname);
}
/**
* Returns the project directory. This directory contains the project.properties file.
......
......@@ -174,8 +174,10 @@ public class YaVersion {
// - LOCATIONSENSOR_COMPONENT_VERSION was incremented to 2
// For YOUNG_ANDROID_VERSION 57:
// - PLAYER_COMPONENT_VERSION was incremented to 4.
// For YOUNG_ANDROID_VERSION 58:
// - FORM_COMPONENT_VERSION was incremented to 7.
public static final int YOUNG_ANDROID_VERSION = 57;
public static final int YOUNG_ANDROID_VERSION = 58;
// ............................... Blocks Language Version Number ...............................
......@@ -339,7 +341,9 @@ public class YaVersion {
// For FORM_COMPONENT_VERSION 6:
// - The SwitchForm and SwitchFormWithArgs methods were removed and the OtherScreenClosed event
// was added.
public static final int FORM_COMPONENT_VERSION = 6;
// For FORM_COMPONENT_VERSION 7:
// - The VersionCode and VersionName properties were added.
public static final int FORM_COMPONENT_VERSION = 7;
public static final int FUSIONTABLESCONTROL_COMPONENT_VERSION = 1;
......
......@@ -689,7 +689,31 @@ public class Form extends Activity
public void Icon(String name) {
// We don't actually need to do anything.
}
/**
* Specifies the Version Code.
*
* @param vCode the version name of the application
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_INTEGER,
defaultValue = "1")
@SimpleProperty(userVisible = false)
public void VersionCode(int vCode) {
// We don't actually need to do anything.
}
/**
* Specifies the Version Name.
*
* @param vName the version name of the application
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
defaultValue = "1.0")
@SimpleProperty(userVisible = false)
public void VersionName(String vName) {
// We don't actually need to do anything.
}
/**
* Width property getter method.
*
......
......@@ -1271,6 +1271,24 @@
the application runs. A natural choice for the title is the title of the App, but
you could make it something else, or even change it while the app is running.
</dd>
<dt>
<code>
VersionCode
</code>
</dt>
<dd>
An integer value which must be incremented each time a new Android Application Package
File (APK) is created for the Google Play Store.
</dd>
<dt>
<code>
VersionName
</code>
</dt>
<dd>
A string which can be changed to allow Google Play Store users to distinguish between
different versions of the App.
</dd>
<dt>
<code class="c2">
Width
......
......@@ -37,6 +37,29 @@
</h1>
Applications built with App Inventor can be uploaded to Google Play!
<dl>
<dt>
<h2>Version Your App</h2>
</dt>
<dd>
<p>
Every app you publish must have a <code>VersionCode</code> and a
<code>VersionName</code>. You can set these in the designer under the
properties panel for the <code>Screen1</code> component.
</p>
<p>
<code>VersionCode</code> is an integer value that will not be visible to
Google Play Store users. It is used by other apps to check if your app
has been upgraded or downgraded. It defaults to 1 and should be increased by
one with every successive change whether it is a major change or a minor change.
</p>
<p>
<code>VersionName</code> is a String which can be anything you would like. It is
defaulted to 1.0. A common structure is a decimal number which is
increased by 1 for every major change and 0.1 for every minor change. For example,
an initial <code>VersionName</code> could be 1.0 which can be updated to 1.1 after
a small change and 2.0 after a larger change.
</p>
</dd>
<dt>
<h2>Download <code>.apk</code></h2>
</dt>
......
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