2017년 3월 31일 금요일

The Blocks Editor in App Inventor 2


Introduction



The Blocks Editor in App Inventor 2 is based off the open source library Blockly, which being written entirely in JavaScript, allows for direct execution in the browser. This means saying goodbye to Java Web Start, and most of the problems caused by not having a proper installation of Java in the users’ machines, including other externally generated problems (such as a particular vendor deciding to turn off web start in all machines after an update).

Before you begin

This guide assumes that the reader is comfortable with the development workflow of App Inventor. Things such as cloning the sources and checking out the appropriate branches (in this case, the main one called master), or creating feature branches for further development are not explained here. Please consult other App Inventor documentation for those matters.


Developing App Inventor 2

An overview of how to get started, main differences with other Blockly apps, structure of the blocklyeditor project, debugging and development tips and tricks, structural changes in the Designer, and communication between GWT and JavaScript.


Note that all the content in this document refers to development having to do with the new blocks editor. Other topics such as component development, or system deployment, have not changed from how they are done in App Inventor classic, so the existing documentation is still relevant for those tasks.


How to get started

The first thing to do is to make yourself familiar with Blockly through their wiki. There are different parts to Blockly that you might or might not need to understand depending on what you are working on (although it’s always good to know what’s going on). For instances, if you are going to be working on UI tasks or issues, you won’t need to know much about how generators work, but you’ll need to understand how the library is loaded, and how the blocks are defined.


Make sure you at least go through the following pages: closure, installation, injection, toolbox, and custom blocks. With regards to toolbox, note that we do NOT use that functionality in AI2 (we use a file called drawer.js instead), but it is good to know how it works in Blockly to avoid confusion if you see code that is different from the examples in the wiki.


If you need to work on the part of the blocks editor that generates YAIL, you’ll want to read about Generating Code. Note that YAIL is not one of the languages supported by Blockly out of the box (JavaScript, Python, and XML are), so we have a set of custom files to make the conversion from blocks to YAIL.


You should also make sure you are comfortable with the overall structure of a Blockly app, by playing around with the demos in the codebase, and also be familiar with the overall philosophy of the language and have a quick look at the FAQ. The other pages in the wiki will also be highly informative, so at least a quick scan would be helpful.


Main differences with Blockly (in brief)

We use drawer.js instead of toolbox.js
We don’t pass parameters to inject


Motivations for the differences:
1) We have components and local variables


2) We have multiple screens, and each load its own instance of Blockly


3) We have some functionality that although it is not in Blockly, we will work (eventually) in contributing it back (if they should want it). Right now there are few things such as arrangements, typeblocking, and RBG colours.


Structure of the blocklyeditor project



Write about the main structure, how it connects to core in appinventor/lib/blockly, how blockly-all.js is generated through ploverConfig.js


Figure: Block Definitions
explain!!!


Figure: generators
explain!!!


Figure: Blockly sources in lib dependencies folder
explain!!!


Can add a few tips to import projects in intelliJ ultimate (although this will be a different doc altogether).


Rebuilding the Project

If you are only making changes in JavaScript, there is no need to re-start the full server.
The target `ant Blockly` will only generate a new blockly-all.js file, which is the file that contains all the JavaScript code for the Blocks Editor. The trick after that is to load it in the browser instead of the old one that is probably cached.
There are two ways to do this loading; Jeff opens the js file directly in a tab in the browser, so that it loads up the new copy. I use a separate browser for development (Chrome Canary) with caching disabled. Pick the one you prefer, or come up with a new recipe! (re-starting the server will also get you there).

If you make changes in any other files, such as html or Java, a full incremental build will be required (using `ant`), and a restart of the server will also be necessary.


Interactive Debugging

Using dev tools. An intro to dev tools here would be good, but we’ve no time for that now. If you don’t know how to use Chrome dev tools or Firebug, read this, complete this course, and watch this video.


Blocklies variable and other tricks:


There is a top variable in the JavaScript window object called ‘Blocklies’. If your project id is ‘/#5629499534213120’ and you are working on Screen1, you can get to the copy of Blockly for that Screen by issuing the following command in dev tools:


bs = Blocklies['5629499534213120_Screen1']


Note that the first part of the string passed in as an index to Blocklies is the project number (whatever yours happens to be), and then a combination of the string ‘_Screen’ finished by the number of the Screen you are working on. For project id #34 and Screen number 5, the string to pass would be ‘34_Screen5’.
That will give you an Object that you can expand to investigate its contents. A few of the things you will want from that object are:
bs.mainWorkspace ← access to the workspace
bs.selected ← access to the particular selected block in the workspace, if any
bs.mainWorkspace.getTopBlocks() ← access to an array with all top blocks in the workspace.


You can see all these in the following figure:


Expanding those objects (through the triangular handle to their left) will provide a view of what’s inside, as in, what kind of attributes and methods they have.


The ‘Sources’ tab of the dev tools allows for code to be shown. If you load the file blockly-all.js, you can search (highlighted in yellow), and you can also set breakpoints for debugging (in blue). The following figure shows that:

How it connects to the Designer
(and how to pass stuff around GWT <--> JavaScript ).


Structural changes in the Designer

Changes to Panels: some modifications to panels have been made for AI2 so this has to be documented (there are quite a few differences with the doc The Browser Client, not only in the addition of the panel for the blocks editor, but also in the changes made to create more screen real state for the editor (changes in the debugging window, the footer, and so on)).


Using an IDE (or not!)

is using a IDE really necessary? this is, after all, a JavaScript project, and IDEs don’t support dynamic languages in the same way that they can support Java, so should you use one? the answers is that it’s really up to you. There’s emacs, vim, and IDE users in the dev team, and we all think our editor is the best (elaborate)


We used Eclipse for a while, we are moving to intellij now, but we still use plain editors.


What are the advantages of using an IDE? additional niceties such as static analysis (limited, but many times helpful), or the in-built support of IntelliJ ultimate for Google Closure (which we use in the project).

Are there other ways to get those advantages in my editor of choice? for the most part, yes.
The limited static analysis that intellij can provide for JavaScript, could be provided by fine grain unit tests.


You can use jsLint or jsHint from emacs and vim (link to plugins). And this should be part of our dev cycle, no doubt!


Blockly and Google Closure

(this section might fit better in the programming section, instead of on its own)
Talk about their relationship, and how Neil switched to G Closure a few months ago and we are still catching up in our own code (we use it if we modify core, but it’s not fully utilised in our language and generator files).


What else?



I don’t know... yet!

Getting information about errors and warnings for blocky code


For my project I need to get the number of warnings and error messages displayed (While working on block editor which can be viewed lower bottom left corner of the viewer window). I can see the code for doing same this behavior in blocky-all.js. But this file is generated runtime.

These are the contents for displaying the number of errors and warnings as text in blocky-all.js

  this.errorCount_ = Blockly.createSvgElement('text',
      {'fill': "black", 'transform':"translate(75,14)"},
      this.svgGroup_);
  this.errorCount_.textContent = "0";

  this.warningCount_ = Blockly.createSvgElement('text',
      {'fill': "black", 'transform':"translate(20,14)"},
      this.svgGroup_);
  this.warningCount_.textContent = "0";

These are the comments I found in the build-common.xml. But I am not sure how this file is being generated.

<!-- ==================================
       blocklyeditor_BlocklyCompile builds:
       - build/blockly-all.js
 ===================================== -->

  <target name="blocklyeditor_BlocklyCompile">
    <ant inheritAll="false" useNativeBasedir="true" dir="${appinventor.dir}/blocklyeditor"
         target="BlocklyCompile"/>
  </target>

Please can anyone suggest easy way for getting this information or point to some sources? Thanks in advance for all the help and time

--
There is some information about the blocks editor in this unfinished
document: The Blocks Editor in App Inventor 2

--
Thanks a lot that was helpful. The code logic related to error and warning counts is in warninghandler.js
--

Creating Apps with Multiple Screens


refer to: Colored Dots for App Inventor 2

A new addition to App Inventor, that was introduced in Release 42, Dec 20, 2011, is the ability to create apps with multiple screens. Creating such apps is not very different from creating apps with only a single screen. In fact, you should think of this process as creating several stand-alone apps which communicate by sending messages to each other.
Every screen that you create has its own components in the Designer window. In the Blocks Editor, you will be able to see only these components and none of the components from the other screens in your app. Similarly, blocks of code related to a screen cannot refer to blocks of code in another screen.
Important note: None of the components, variable definitions, and procedures that you define in one screen will be accessible from a second screen.
To explain how to build an app with two screens, we will use the running example of an app titled ColoredDots. ColoredDots is similar to PaintPot, but it uses a second screen to allow the user to create new colors by providing values for the basic colors red, green, and blue. In the PaintPot app, a user can only paint with one of the three predefined colors. Adding more colors would have reduced the amount of space available to painting, requiring new buttons on the screen. In ColoredDots, the ability of choosing a new color is implemented with a second screen, Settings. Once a new color is created, its value is passed to the first screen through a message.
These are the two screens for the ColoredDots app:
Painting Screen
Settings Screen

Working with the Main Screen (Screen1)


When you create a project, App Inventor automatically creates the main screen and names it Screen1. While you can change its title in the Properties pane, the Rename button for Screen1 is disabled, so the only way to refer to it is as Screen1. In the following image, you see a screenshot of the Designer window when Screen1 is selected. You can notice that Screen1 is selected because the button Screen1 (highlighted with a red circle) is in a dark-green color.
Screen1 Designer Window
As you can see in the Components pane, only the components that belong to Screen1 are accessible. Additionally, in the Blocks Editor, you will only find these components as well, as the below image shows. The shown code, implements the basic behavior of the main screen.
Screen1 Blocsk Editor
Notice the title shown in the top right corner of the window: ColoredDots - Screen1. Always check that title, to make sure you are looking at the code for the correspoding screen that is open in the Designer window.

Adding a new screen


To add a new screen to your app, you click on the Add Screen button in the top toolbar of the Designer window, see left-side image below. A dialog window will appear, in which you can provide a name for the new screen as shown in the right-side image below.
Adding a new screen
Naming new screen
Important Note: You should change the name of the screen to something meaningful in the moment you add a new screen to the app. Once a screen is added, its name cannot be changed.

Working with the new screen


When you create a new screen, the Designer window switches content to display the new screen. At the beginning there will be no components in it. You continue adding new components as you do whenever you create a new app. The image below shows the Designer window for the SettingsScreen after all its components have been added.
Second screen - Designer window
When you switch to the Blocks Editor, you will notice that only the components for the SettingsScreen are shown, and you can write the code to implement its behavior.
Second screen - Blocks editor
Now that we know how to create multiple screens, let's explain how the two screens with communicate with each-other.

Communication between screens


Most of the methods needed to implement communication between screens are found in the Control drawer in the Blocks Editor. Additionally, there is an event handler OtherScreenClosed for the screen components.
Methods of working with screens
Other Screen Closed event handler
The arguments to such methods and the event handlers are always text, either the screen name, or a message. While it is possible to pass as arguments other data types, such as numbers or lists, their values will be automatically converted to text.
Here are examples of using the open/close methods in our app. The method open another screen is used in Screen1 by the event handler SettingsButton.Click. The close screen methods are used by the two buttons of the SettingsScreen.
Open screen method
Close screen methods
Then, in the code of Screen1, we need to make sure that the event handler OtherScreenClosed is fleshed out. Below is the code for Screen1. In the resultblock the color value from the closed SettingsScreen is passed, and then used to change the paint color for the Canvas1 component. Notice that since we have only one other screen, we didn't use the argument otherScreenName. You will need to check its value whenever there are more than one other screens reporting back to this screen.
Close screen methods

Known Problems


In the current version, it is not yet possible to test the multiple-screen app in the development mode. You can test each screen separately, but you cannot test the communication between two screens. You will receive a message as shown below:
Warning message
In order to test whether the screens are talking to each other, you need to Package for Phone and open the installed app.

Miscellaneous

  • You might have noticed the procedure CheckColorValue used in the event handler for TestButton. It was used in order to avoid bugs that will be introduced if the user forgets to enter a value in one of the text boxes (or all of them), or enters a meaningless number (values must be between 0 and 255). Since we cannot rely on the fact that users will be careful, we check for those values with a dedicated procedure, see its code below:
    CheckColorValue procedure
  • Our Settings screen currently returns only one value, for the paint color. Usually, in a settings screen, we might have several options that can be changed. For example, in our app, we could have an input field that allows us to change the radius of the dots, or the addition of a background image for the canvas. Because the messages communicated between two screens are text values, you will need to write extra code to make sure that you handle additional data properly. Below is the code for the two screens, if we add a second setting in our app that allows the user to enter the radius for the dots. Notice the use oflist to csv row and list from csv row to convert the list to text and the text to list.
    Using list to csv
    Using list from csv
  • You can achieve the same result by using TinyDB. That is, you store the setting values in the TinyDB and then read them in the next screen. However, in order for this to work, you will need to have a TinyDB component in every screen you create, since the screens cannot refer to each other components.
  • One of the methods available for screen communication is open another screen with start text. To use this start text in the new screen, in its Screen.Initialize event handler, you will need to call the method get start text.

Cannot Find Symbol for blokytalky


Hey everyone! I have a compiler error ,and I am new to AppInventor so not sure where to look. Here is the error:

AiClientLib:
   [depend] Deleted 2 out of date files in 0 seconds
    [javac] Compiling 16 source files to /Users/zach/CodingProjects/newAppInv/appinventor-sources/appinventor/appengine/build/war/WEB-INF/classes
    [javac] /Users/zach/CodingProjects/newAppInv/appinventor-sources/appinventor/build/components/ComponentTranslation/src/com/google/appinventor/client/ComponentsTranslation.java:156: error: cannot find symbol
    [javac]     map.put("PROPERTY-NodeName", MESSAGES.NodeNameProperties());
    [javac]                                          ^
    [javac]   symbol:   method NodeNameProperties()
    [javac]   location: variable MESSAGES of type OdeMessages
    [javac] /Users/zach/CodingProjects/newAppInv/appinventor-sources/appinventor/build/components/ComponentTranslation/src/com/google/appinventor/client/ComponentsTranslation.java:161: error: cannot find symbol
    [javac]     map.put("EVENT-OnMessageReceived", MESSAGES.OnMessageReceivedEvents());
    [javac]                                                ^
    [javac]   symbol:   method OnMessageReceivedEvents()
    [javac]   location: variable MESSAGES of type OdeMessages
    [javac] Note: /Users/zach/CodingProjects/newAppInv/appinventor-sources/appinventor/appengine/src/com/google/appinventor/client/editor/youngandroid/BlocklyPanel.java uses or overrides a deprecated API.
    [javac] Note: Recompile with -Xlint:deprecation for details.
    [javac] 2 errors

From what I can tell, Java cannot find the properties of MESSAGES ,but I am not sure where to go from here.

--
Hi there, it indeed looks like an issue with missing messages for internationalisation.
Are you writing your own component? If so, make sure you check out how to deal with messages in the docs.
If not, are you in latest master?

--
I am making my own component. I will check there first ,and report back. Thank you! :)

--
I am making my own component
or are you creating an extension?
For extensions: internationalisation is not relevant...
probably a short snippet of your code could help us to help you...

-- 
Apologies for the late response. Yes, I am making an extension. Here is my source code: https://github.com/ZachLamb/appinventor-sources

--
unfortunately it's very difficult to try and figure out what you are
doing from your github fork. It's easier if you work off a feature
branch instead of working directly on msater, so all your commits can
be seen at once.

Also, it seems like you haven't copied the sample.gitignore file into
a .gitignore file, so you have done a commit of things like .class
files. Your initial commit has changes in 300 files? It would be a lot
easier if you can clean up htat master branch and set git
appropriately.

--
Thank you for your response and feedback. As you can tell, I am very naive with git. I have added a gitignore based off of the sample gitignore. I also added a new branch and added in commits with comments on where I have made changes such as adding in the build files and a new file for the component that I am trying to make into an extension. https://github.com/ZachLamb/appinventor-sources/tree/blockyTalky. Does that help? 

--
Hi there, you haven't really cleaned up the first two commits, so you
still have class files all over the place. By the last two commits I
can see that you have a new file in runtime BlocklyTalky.java and have
modified a build file... anything else I am missing?

Have you modified GitBuildId or the ComponentsTranslation classes?
-- 
Hi, I actually have modified three build files. The one directly under the appinventor folder, the one under the BuildServer folder, and the one under the Components folder. No, I have not modified those files.

--
Hey Zack, are you sure this is an extension? (as in an App Inventor
Externsion aix?) It looks like a component, but I am not too familiar
with extensions, as I haven't had the time to try and write one yet.

If it's a component, you need to add the internationalisation code for
this to work; please see the notes at the end of this document:
https://docs.google.com/document/d/1xk9dMfczvjbbwD-wMsr-ffqkTlE3ga0ocCE1KOb2wvw/pub#h.sajmtj7l1183
--
if you want to create an extension, probably a good idea is to try one of the available source codes, modify the package name and build it to see, if building an extension works fine for you. You can find source code for example extensions here http://appinventor.mit.edu/extensions

Then if you managed to build a simple extension successfully,  try an example extension, which uses an external library, for example the SoundAnalysis Extension.

Then if that also works fine, continue with your own extension...

--
Hey Taifun (and Jose),

Thank you both for helping me troubleshoot this ,and being patient with me. I apologize for the late response. I am an undergraduate ,and I have been working on final projects and studying for finals these past couple of weeks. I am taking your advice ,and starting from the ground up. :)

--

Singleton BlockyTalky Component


Is there a procedure or precedent for creating singleton components in AppInventor? 

We're creating a BlockyTalky extension for AI. Currently, BlockyTalky entities interact by declaring their existence to a central server and communicating to each other via that server. Our current implementation makes the phone into a BlockyTalky entity and allows students to send and receive messages by naming a destination entity. Given this architecture, it doesn't make sense for a phone to have multiple BlockyTalky components, but the default behavior allows for multiples to be created.

Is it possible to enforce a single instance of a component? Is it advisable?

--
I am no expert in App Inventor but with some reading from this group, I found out that singleton components are not yet supported by AI. The reason is that AI treats every screen as separate app itself. This link might help you out.

--
There's been some talk in the fairly recent past about implementing the ability to check (and disallow) for duplicate singleton components but it hasn't been done yet.  So no, it's not currently enforceable and yes, it is advisable.

There are currently a number of components that are essentially (or should be) singletons.  They usually deal with the issue by making making certain fields and methods in the class be static.

--
Just to clarify, each Screen in App Inventor is a separate Android Activity, which is not quite the same thing as being a separate app.


--
yup, it would be cool to have an annotation to deal with this. Would
you guys be up to looking into it? We can provide some guidelines if
that helps.

--

Where’s MIT?


Use App Inventor 2 to search from the Big Bang to locate the Massachusetts Institute of Technology (MIT) within the Universe
Abe Getzler
June 2016


Sample run

Start at the Big Bang

The Big Bang.png
We start with a Web Viewer pointing to a web page on The Big Bang, and a List Picker set to show the super clusters that resulted from The Big Bang .


Children of The Big Bang

After The Big Bang.png


This List Picker is loaded with the names of the major sub components of the item that was last picked, followed by the trail of prior items, to let the user go back from a poor choice.


After Picking Laniakea Super Cluster

Laniakea Supercluster.png
Here we have picked the Laniakea Super Cluster, and the Web Viewer and List Picker are reloaded to show our selection and its possible children.


Program Structure

Designer

Designer.png


There are only three components:
  1. A Web Viewer to show details of our choice
  2. A List Picker to show our navigation options
  3. A File for loading our navigation map.


Screen1.Initialize

Screen1_Initialize.png


At startup time, we start loading our csv formatted navigation map file from the Media Drawer.


Wheres_MIT.csv

Microsoft Excel - Wheres_MIT.csv.png


Spreadsheet programs make it easy to edit a navigational map file and export it in Comma Separated Value (CSV) format.  This map is set up with parent name in column 1, child name in column 2, and child URL in column 3.


Column 2 is our unique key to this table.


File1.GotText

File1_GotText.png
When the csv text map arrives, we save it into a global text variable, in case we need to diagnose it later.  Then we send it through a list from csv table block to convert it into a table (list of lists).
The first column of the first row is reserved for the starting point of our tour, the root.
We visit the root.

Global map and map_text

global map and map_text.png

Stack and Root

stack and root.png


The stack global will hold a list of all the names of places we have visited, starting at the root, newest at the front (position 1).


Visit

visit.png
Given the name of the item in our map we want to visit, we first extract the children of that key from our map.  We push our key onto our stack at slot 1, look up its URL in our map, and have the Web Viewer show it.  We then show our current key in the List Picker, as well as all its children in the stack and the List Picker Elements.


Children

children.png
This is a value procedure, returning a simple list of the text values in column 2 of our map where column 1 matches our key.


URL



URL.png
The URL of a key is the third column of the row in our map that belongs to that key.


Row

row.png
The row  value procedure returns a simple 3 element list containing a copy of the row in the map that matches the given key in column 2.


ListPicker1.AfterPicking

ListPicker1_AfterPicking.png
After the user selects a destination name from the List Picker, we visit that selection.


All the blocks



See this app in The Gallery: