AI2 Component  (Version nb184)
Voting.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
24 
25 import android.app.Activity;
26 import android.os.Handler;
27 import android.util.Log;
28 
29 import org.apache.http.NameValuePair;
30 import org.apache.http.message.BasicNameValuePair;
31 import org.json.JSONArray;
32 import org.json.JSONException;
33 import org.json.JSONObject;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
72 @DesignerComponent(version = YaVersion.VOTING_COMPONENT_VERSION,
73  designerHelpDescription = "<p>The Voting component enables users to vote " +
74  "on a question by communicating with a Web service to retrieve a ballot " +
75  "and later sending back users' votes.</p>",
76  category = ComponentCategory.INTERNAL, // moved to Internal until fully tested
77  nonVisible = true,
78  iconName = "images/voting.png")
79 @SimpleObject
80 @UsesPermissions(permissionNames = "android.permission.INTERNET")
81 
82 public class Voting extends AndroidNonvisibleComponent implements Component {
83  private static final String LOG_TAG = "Voting";
84  private static final String REQUESTBALLOT_COMMAND = "requestballot";
85  private static final String SENDBALLOT_COMMAND = "sendballot";
86  private static final String IS_POLLING_PARAMETER = "isPolling";
87  private static final String ID_REQUESTED_PARAMETER = "idRequested";
88  private static final String BALLOT_QUESTION_PARAMETER = "question";
89  private static final String BALLOT_OPTIONS_PARAMETER = "options";
90  private static final String USER_CHOICE_PARAMETER = "userchoice";
91  private static final String USER_ID_PARAMETER = "userid";
92 
93  private Handler androidUIHandler;
94  private ComponentContainer theContainer;
95  private Activity activityContext;
96 
97  private String userId;
98  private String serviceURL;
99  private String ballotQuestion;
100  private String ballotOptionsString;
101 
102  // The choices that a vote selects among
103  private ArrayList<String> ballotOptions;
104 
105  // TODO(halabelson): idRequested isn't used in this version, but we'll keep it for the future
106  private Boolean idRequested;
107  private String userChoice;
108  private Boolean isPolling;
109 
110  public Voting(ComponentContainer container){
111  super(container.$form());
112  serviceURL = "http://androvote.appspot.com";
113  userId = "";
114  isPolling = false;
115  idRequested = false;
116  ballotQuestion = "";
117  ballotOptions = new ArrayList<String>();
118  userChoice = "";
119 
120  androidUIHandler = new Handler();
121  theContainer = container;
122  activityContext = container.$context();
123 
124  // We set the initial value of serviceURL to be the
125  // demo Web service
126  serviceURL = "http://androvote.appspot.com";
127  }
128 
133  description = "The URL of the Voting service",
134  category = PropertyCategory.BEHAVIOR)
135  public String ServiceURL() {
136  return serviceURL;
137  }
138 
145  defaultValue = "http://androvote.appspot.com")
147  public void ServiceURL(String serviceURL) {
148  this.serviceURL = serviceURL;
149  }
150 
155  description = "The question to be voted on.",
156  category = PropertyCategory.BEHAVIOR)
157  public String BallotQuestion() {
158  return ballotQuestion;
159  }
160 
165  description = "The list of ballot options.",
166  category = PropertyCategory.BEHAVIOR)
167  public List<String> BallotOptions(){
168  return ballotOptions;
169  }
170 
171 
172  // This should not be settable by the user
173  // @SimpleProperty
174  // public void BallotOptions(String ballotOptions){
175  // this.ballotOptions = ballotOptions;
176  // }
177 
182  description = "A text identifying the voter that is sent to the Voting " +
183  "server along with the vote. This must be set before " +
184  "<code>SendBallot</code> is called.",
185  category = PropertyCategory.BEHAVIOR)
186  public String UserId() {
187  return userId;
188  }
189 
196  public void UserId(String userId){
197  this.userId = userId;
198  }
199 
204  description = "The ballot choice to send to the server, which must be " +
205  "set before <code>SendBallot</code> is called. " +
206  "This must be one of <code>BallotOptions</code>.",
207  category = PropertyCategory.BEHAVIOR)
208  public String UserChoice() {
209  return userChoice;
210  }
211 
218  public void UserChoice(String userChoice){
219  this.userChoice = userChoice;
220  }
221 
227  description = "The email address associated with this device. This property has been " +
228  "deprecated and always returns the empty text value.",
229  category = PropertyCategory.BEHAVIOR)
230  public String UserEmailAddress() {
231  // The UserEmailAddress has not been supported since before the Gingerbread release, so we
232  // suspect that nobody is relying on it, and are therefore deprecating it. If it happens that
233  // it needs to be added back, the way to get an email address, is to force the user to select
234  // an account. This has to be done asynchronously (not here on the UI thread), generally when
235  // the application starts. However, that would mean that the application would always ask the
236  // user to select an account at startup, even if the application never actually accesses this
237  // property, which would possibly be alarming to the user of a Voting application.
238  return "";
239  }
240 
241  /* RequestBallot will talk to the Web service and retrieve the ballot of
242  * the current open poll. Depending on the service response, two events
243  * might be triggered: NoOpenPoll or GotBallot.
244  * When a ballot is received, the JSON response looks like this:
245  * {"isPolling" : "true",
246  * "idRequested" : "true",
247  * "question" : "What are you?",
248  * "options": [ "I'm a PC", "I'm a Mac" ] }
249  */
250 
255  description =
256  "Send a request for a ballot to the Web service specified " +
257  "by the property <code>ServiceURL</code>. When the " +
258  "completes, one of the following events will be raised: " +
259  "<code>GotBallot</code>, <code>NoOpenPoll</code>, or " +
260  "<code>WebServiceError</code>.")
261  public void RequestBallot() {
262  final Runnable call = new Runnable() {
263  public void run() { postRequestBallot(); }};
265  }
266 
267  private void postRequestBallot(){
269  public void onSuccess(JSONObject result) {
270  if (result == null) {
271  // Signal a Web error event to indicate that there was no response
272  // to this request for a ballot.
273  androidUIHandler.post(new Runnable() {
274  public void run() {
275  WebServiceError("The Web server did not respond to your request for a ballot");
276  }
277  });
278  return;
279  } else {
280  try {
281  Log.i(LOG_TAG, "postRequestBallot: ballot retrieved " + result);
282  // The Web service is designed to return the JSON encoded object
283  // This has to be a legal JSON encoding. For example, true and false
284  // should not be quoted if we're using getBoolean. A bad encoding will
285  // throw a JSON exception.
286  isPolling = result.getBoolean(IS_POLLING_PARAMETER);
287  if (isPolling){
288  //populate parameter's value directly from reading JSONObject
289  idRequested = result.getBoolean(ID_REQUESTED_PARAMETER);
290  ballotQuestion = result.getString(BALLOT_QUESTION_PARAMETER);
291  ballotOptionsString = result.getString(BALLOT_OPTIONS_PARAMETER);
292  ballotOptions = JSONArrayToArrayList(new JSONArray(ballotOptionsString));
293  androidUIHandler.post(new Runnable() {
294  public void run() {
295  GotBallot();
296  }
297  });
298  } else {
299  androidUIHandler.post(new Runnable() {
300  public void run() {
301  NoOpenPoll();
302  }
303  });
304  }
305  } catch (JSONException e) {
306  // Signal a Web error event to indicate the the server
307  // returned a garbled value. From the user's perspective,
308  // there may be no practical difference between this and
309  // the "no response" error above, but application writers
310  // can create handlers to use these events as they choose.
311  // Note that server errors that create malformed JSON
312  // responses will sometimes be caught here.
313  androidUIHandler.post(new Runnable() {
314  public void run() {
315  WebServiceError("The Web server returned a garbled object");
316  }
317  });
318  return;
319  }
320  }
321  }
322  public void onFailure(final String message) {
323  Log.w(LOG_TAG, "postRequestBallot Failure " + message);
324  androidUIHandler.post(new Runnable() {
325  public void run() {
326  WebServiceError(message);
327  }
328  });
329  return;
330  }
331  };
332 
333  WebServiceUtil.getInstance().postCommandReturningObject(
334  serviceURL,
335  REQUESTBALLOT_COMMAND,
336  null,
337  myCallback);
338  return;
339  }
340 
341  private ArrayList<String> JSONArrayToArrayList(JSONArray ja) throws JSONException {
342  ArrayList<String> a = new ArrayList<String>();
343  for (int i = 0; i < ja.length(); i++) {
344  a.add(ja.getString(i));
345  }
346  return a;
347  }
348 
349 
353  @SimpleEvent(
354  description =
355  "Event indicating that a ballot was retrieved from the Web " +
356  "service and that the properties <code>BallotQuestion</code> and " +
357  "<code>BallotOptions</code> have been set. This is always preceded " +
358  "by a call to the method <code>RequestBallot</code>.")
359  public void GotBallot() {
360  EventDispatcher.dispatchEvent(this, "GotBallot");
361  }
362 
366  @SimpleEvent
367  public void NoOpenPoll() {
368  EventDispatcher.dispatchEvent(this, "NoOpenPoll");
369  }
370 
376  description =
377  "Send a completed ballot to the Web service. This should " +
378  "not be called until the properties <code>UserId</code> " +
379  "and <code>UserChoice</code> have been set by the application.")
380  public void SendBallot() {
381  final Runnable call = new Runnable() {
382  public void run() { postSendBallot(userChoice, userId); }};
384  }
385 
386  private void postSendBallot(String userChoice, String userId){
388  // the Web service will send back a confirmation message, but
389  // the component ignores it and notes only that anything at
390  // all was sent back. We can improve this later.
391  public void onSuccess(String response) {
392  androidUIHandler.post(new Runnable() {
393  public void run() {
394  GotBallotConfirmation();
395  }
396  });
397  }
398  public void onFailure(final String message) {
399  Log.w(LOG_TAG, "postSendBallot Failure " + message);
400  androidUIHandler.post(new Runnable() {
401  public void run() {
402  WebServiceError(message);
403  }
404  });
405  return;
406  }
407  };
408 
409  WebServiceUtil.getInstance().postCommand(serviceURL,
410  SENDBALLOT_COMMAND,
411  Lists.<NameValuePair>newArrayList(
412  new BasicNameValuePair(USER_CHOICE_PARAMETER, userChoice),
413  new BasicNameValuePair(USER_ID_PARAMETER, userId)),
414  myCallback);
415 
416  }
417 
421  @SimpleEvent
422  public void GotBallotConfirmation() {
423  EventDispatcher.dispatchEvent(this, "GotBallotConfirmation");
424  }
425 
426  //-----------------------------------------------------------------------------
433  @SimpleEvent
434  public void WebServiceError(String message) {
435  // Invoke the application's "WebServiceError" event handler
436  EventDispatcher.dispatchEvent(this, "WebServiceError", message);
437  }
438 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.Voting.NoOpenPoll
void NoOpenPoll()
Definition: Voting.java:367
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.runtime.Voting.WebServiceError
void WebServiceError(String message)
Definition: Voting.java:434
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.Voting.ServiceURL
void ServiceURL(String serviceURL)
Definition: Voting.java:147
com.google.appinventor.components.runtime.util.WebServiceUtil
Definition: WebServiceUtil.java:45
com.google.appinventor.components.runtime.Voting.UserId
void UserId(String userId)
Definition: Voting.java:196
com.google.appinventor.components.runtime.collect
Definition: Lists.java:7
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.util.AsynchUtil.runAsynchronously
static void runAsynchronously(final Runnable call)
Definition: AsynchUtil.java:23
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.collect.Lists
Definition: Lists.java:20
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.runtime.util.AsynchUtil
Definition: AsynchUtil.java:17
com.google
com
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.Voting.UserChoice
void UserChoice(String userChoice)
Definition: Voting.java:218
com.google.appinventor.components.runtime.Voting.GotBallotConfirmation
void GotBallotConfirmation()
Definition: Voting.java:422
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor.components.runtime.Voting
Definition: Voting.java:82
com.google.appinventor.components.runtime.util.AsyncCallbackPair
Definition: AsyncCallbackPair.java:17
com.google.appinventor
com.google.appinventor.components.runtime.Voting.Voting
Voting(ComponentContainer container)
Definition: Voting.java:110