Proposal to render Android App Inventor visual code blocks as pseudo-Python code

by Philip Guo (pg@cs.stanford.edu)

I think it would be a good idea to add a feature to the Android App Inventor GUI blocks editor to render the visual code blocks as pseudo-Python code (in, say, a pop-up window).

Doing so improves readability, especially for complex blocks with lots of nested expressions. Also, it gives students practice reading code in textual form, which could help them transition to more advanced programming courses.

The rendered pseudo-Python code would be read-only; students would still edit code visually using the blocks editor. The purpose of the Python code is merely illustrative; it is obviously not meant to be executable.


Prototype:

Since I do not have access to the App Inventor source code, I wrote a prototype script (with an online demo) that converts the compiled .yail files into pseudo-Python code. (App Inventor compiles the visual code blocks into .yail files, a Scheme-based intermediate representation, so those files contain all the information present in the blocks.)

Below I show some code blocks from tutorials that my script rendered as pseudo-Python code. Please let me know if this seems like a useful feature to implement in App Inventor.

No Text While Driving

pseudo-Python:

response = 'text'

when Screen1.Initialize():
  response = TinyDB1.GetValue('responseMessage')
  if (len(response) > 0):
    MessageTextbox.Text = response

when SubmitResponseButton.Click():
  TinyDB1.StoreValue('responseMessage', MessageTextbox.Text)

when Texting1.MessageReceived(number, messageText):
  Texting1.PhoneNumber = number
  Texting1.Message = MessageTextbox.Text
  Texting1.SendMessage()
No Text While Driving, Part 2

pseudo-Python:

lastKnownLocation = 'unknown'

response = 'text'

when Screen1.Initialize():
  response = TinyDB1.GetValue('responseMessage')
  if (len(response) > 0):
    MessageTextbox.Text = response

when SubmitResponseButton.Click():
  TinyDB1.StoreValue('responseMessage', MessageTextbox.Text)

when Texting1.MessageReceived(number, messageText):
  Texting1.PhoneNumber = number
  Texting1.Message = MessageTextbox.Text + ' My location is ' + lastKnownLocation
  Texting1.SendMessage()
  TextToSpeech1.Speak('message from ' + number + '  ' + messageText)

when LocationSensor1.LocationChanged(latitude, longitude, altitude):
  lastKnownLocation = LocationSensor1.CurrentAddress
Broadcaster Hub

pseudo-Python:

BroadcastList = []

def displayBroadcastList():
  BroadcastListLabel.Text = 'Phone Numbers...'
  for pnumber in BroadcastList:
    BroadcastListLabel.Text = BroadcastListLabel.Text + '\n' + pnumber

valueFromDB = 'text'

when Screen1.Initialize():
  valueFromDB = TinyDB1.GetValue('broadcastList')
  if (len(valueFromDB) > 0):
    BroadcastList = valueFromDB
    displayBroadcastList()

when Texting1.MessageReceived(number, messageText):
  if (number in BroadcastList):
    Texting1.Message = messageText
    for var in BroadcastList:
      Texting1.PhoneNumber = var
      Texting1.SendMessage()
    LogLabel.Text = 'message from:' + number + ' broadcast\n' + LogLabel.Text
  else:
    Texting1.PhoneNumber = number
    if (messageText == 'joinabc'):
      BroadcastList.append(number)
      displayBroadcastList()
      Texting1.Message = 'Congrats, you have joined the abc broadcast list'
      TinyDB1.StoreValue('broadcastList', BroadcastList)
    else:
      Texting1.Message = " To join this broadcast list, text 'joinabc' to this number."
    Texting1.SendMessage()
Android, Where's My Car?

pseudo-Python:

tempAddress = 'text'

when Screen1.Initialize():
  tempAddress = TinyDB1.GetValue('address')
  if (len(tempAddress) > 0):
    RememberedAddressDataLabel.Text = tempAddress
    RememberedLatLabel.Text = TinyDB1.GetValue('lat')
    RememberedLongLabel.Text = TinyDB1.GetValue('long')
    DirectionsButton.Enabled = True

when RememberButton.Click():
  RememberedAddressDataLabel.Text = LocationSensor1.CurrentAddress
  RememberedLatLabel.Text = LocationSensor1.Latitude
  RememberedLongLabel.Text = LocationSensor1.Longitude
  TinyDB1.StoreValue('address', LocationSensor1.CurrentAddress)
  TinyDB1.StoreValue('lat', LocationSensor1.Latitude)
  TinyDB1.StoreValue('long', LocationSensor1.Longitude)
  DirectionsButton.Enabled = True

when DirectionsButton.Click():
  ActivityStarter1.DataUri = 'http://maps.google.com/maps?saddr=' \
                             + CurrentLatLabel.Text + ',' \
                             + CurrentLongLabel.Text + '&daddr=' \
                             + RememberedLatLabel.Text + ',' \
                             + RememberedLongLabel.Text
  ActivityStarter1.StartActivity()

when LocationSensor1.LocationChanged(latitude, longitude, altitude):
  CurrentAddressDataLabel.Text = LocationSensor1.CurrentAddress
  CurrentLatLabel.Text = latitude
  CurrentLongLabel.Text = longitude
  RememberButton.Enabled = True
Text Group Part II: Adding and Removing Members

pseudo-Python:

def displayMembers():
  MembersLabel.Text = ''
  for number in PhoneNumbers:
    MembersLabel.Text = MembersLabel.Text + '\n' + number

valueFromDB = ''

PhoneNumbers = []

when Screen1.Initialize():
  valueFromDB = TinyDB1.GetValue('textGroup')
  if (type(valueFromDB) is list):
    PhoneNumbers = valueFromDB
    displayMembers()

when TextGroupButton.Click():
  Texting1.Message = MessageText.Text
  for var in PhoneNumbers:
    Texting1.PhoneNumber = var
    Texting1.SendMessage()
  StatusLabel.Text = 'last message sent:' + MessageText.Text

when PhoneNumberPicker1.AfterPicking():
  PhoneNumbers.append(PhoneNumberPicker1.PhoneNumber)
  TinyDB1.StoreValue('textGroup', PhoneNumbers)
  displayMembers()

when RemoveListPicker.BeforePicking():
  RemoveListPicker.Elements = PhoneNumbers

when RemoveListPicker.AfterPicking():
  yail-list-remove-item!(PhoneNumbers, yail-list-index(RemoveListPicker.Selection, PhoneNumbers))
  TinyDB1.StoreValue('textGroup', PhoneNumbers)
  displayMembers()