AI2 Component  (Version nb184)
Form.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-2020 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 
9 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
10 import static android.Manifest.permission.ACCESS_WIFI_STATE;
11 import static android.Manifest.permission.INTERNET;
13 
14 import android.Manifest;
15 import android.annotation.SuppressLint;
16 import android.app.Activity;
17 import android.app.Dialog;
18 import android.app.ProgressDialog;
19 import android.content.ActivityNotFoundException;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.res.AssetManager;
26 import android.content.res.Configuration;
27 import android.graphics.PorterDuff;
28 import android.graphics.drawable.ColorDrawable;
29 import android.graphics.drawable.Drawable;
30 import android.os.AsyncTask;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.util.Log;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.MenuItem.OnMenuItemClickListener;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
41 import android.view.WindowManager;
42 import android.view.inputmethod.InputMethodManager;
43 import android.widget.FrameLayout;
44 import android.widget.ScrollView;
45 import androidx.annotation.VisibleForTesting;
46 import androidx.core.app.ActivityCompat;
47 import androidx.core.content.ContextCompat;
48 
49 import com.google.appinventor.common.version.AppInventorFeatures;
50 
81 
82 import java.io.FileNotFoundException;
83 import java.io.IOException;
84 import java.io.InputStream;
85 import java.lang.reflect.InvocationTargetException;
86 import java.lang.reflect.Method;
87 import java.net.URI;
88 import java.util.ArrayList;
89 import java.util.Collections;
90 import java.util.HashMap;
91 import java.util.HashSet;
92 import java.util.Iterator;
93 import java.util.LinkedHashMap;
94 import java.util.List;
95 import java.util.Map;
96 import java.util.Random;
97 import java.util.Set;
98 
99 import org.json.JSONException;
100 
101 
120 @DesignerComponent(version = YaVersion.FORM_COMPONENT_VERSION,
121  category = ComponentCategory.USERINTERFACE,
122  description = "Top-level component containing all other components in the program",
123  showOnPalette = false)
124 @SimpleObject
125 @UsesPermissions({INTERNET, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE})
126 public class Form extends AppInventorCompatActivity
128  OnGlobalLayoutListener {
129 
130  private static final String LOG_TAG = "Form";
131 
132  private static final String RESULT_NAME = "APP_INVENTOR_RESULT";
133 
134  private static final String ARGUMENT_NAME = "APP_INVENTOR_START";
135 
136  public static final String APPINVENTOR_URL_SCHEME = "appinventor";
137 
138  public static final String ASSETS_PREFIX = "file:///android_asset/";
139 
140  private static final int DEFAULT_PRIMARY_COLOR_DARK =
142  private static final int DEFAULT_ACCENT_COLOR =
144 
145  // Keep track of the current form object.
146  // activeForm always holds the Form that is currently handling event dispatching so runtime.scm
147  // can lookup symbols in the correct environment.
148  // There is at least one case where an event can be fired when the activity is not the foreground
149  // activity: if a Clock component's TimerAlwaysFires property is true, the Clock component's
150  // Timer event will still fire, even when the activity is no longer in the foreground. For this
151  // reason, we cannot assume that the activeForm is the foreground activity.
152  protected static Form activeForm;
153 
154  private float deviceDensity;
155  private float compatScalingFactor;
156 
157  // applicationIsBeingClosed is set to true during closeApplication.
158  private static boolean applicationIsBeingClosed;
159 
160  protected final Handler androidUIHandler = new Handler();
161 
162  protected String formName;
163 
164  private boolean screenInitialized;
165 
166  private static final int SWITCH_FORM_REQUEST_CODE = 1;
167  private static int nextRequestCode = SWITCH_FORM_REQUEST_CODE + 1;
168 
169  // Backing for background color
170  private int backgroundColor;
171 
172  // Information string the app creator can set. It will be shown when
173  // "about this application" menu item is selected.
174  private String aboutScreen;
175  private boolean showStatusBar = true;
176  private boolean showTitle = true;
177  protected String title = "";
178 
179  private String backgroundImagePath = "";
180  private Drawable backgroundDrawable;
181  private boolean usesDefaultBackground;
182  private boolean usesDarkTheme;
183 
184  // Layout
185  private LinearLayout viewLayout;
186 
187  // translates App Inventor alignment codes to Android gravity
188  private AlignmentUtil alignmentSetter;
189 
190  // the alignment for this component's LinearLayout
191  private int horizontalAlignment;
192  private int verticalAlignment;
193 
194  // String representing the transition animation type
195  private String openAnimType;
196  private String closeAnimType;
197 
198  // Syle information
199  private int primaryColor = DEFAULT_PRIMARY_COLOR;
200  private int primaryColorDark = DEFAULT_PRIMARY_COLOR_DARK;
201  private int accentColor = DEFAULT_ACCENT_COLOR;
202 
203  private FrameLayout frameLayout;
204  private boolean scrollable;
205 
206  private ScaledFrameLayout scaleLayout;
207  private static boolean sCompatibilityMode;
208 
209  private static boolean showListsAsJson;
210 
211  private final Set<String> permissions = new HashSet<String>();
212 
213  // Application lifecycle related fields
214  private final HashMap<Integer, ActivityResultListener> activityResultMap = Maps.newHashMap();
215  private final Map<Integer, Set<ActivityResultListener>> activityResultMultiMap = Maps.newHashMap();
216  private final Set<OnStopListener> onStopListeners = Sets.newHashSet();
217  private final Set<OnClearListener> onClearListeners = Sets.newHashSet();
218  private final Set<OnNewIntentListener> onNewIntentListeners = Sets.newHashSet();
219  private final Set<OnResumeListener> onResumeListeners = Sets.newHashSet();
220  private final Set<OnOrientationChangeListener> onOrientationChangeListeners = Sets.newHashSet();
221  private final Set<OnPauseListener> onPauseListeners = Sets.newHashSet();
222  private final Set<OnDestroyListener> onDestroyListeners = Sets.newHashSet();
223 
224  // AppInventor lifecycle: listeners for the Initialize Event
225  private final Set<OnInitializeListener> onInitializeListeners = Sets.newHashSet();
226 
227  // Listeners for options menu.
228  private final Set<OnCreateOptionsMenuListener> onCreateOptionsMenuListeners = Sets.newHashSet();
229  private final Set<OnOptionsItemSelectedListener> onOptionsItemSelectedListeners = Sets.newHashSet();
230 
231  // Listeners for permission results
232  private final HashMap<Integer, PermissionResultHandler> permissionHandlers = Maps.newHashMap();
233 
234  private final Random permissionRandom = new Random(); // Used for generating nonces
235 
236  // Set to the optional String-valued Extra passed in via an Intent on startup.
237  // This is passed directly in the Repl.
238  protected String startupValue = "";
239 
240  // To control volume of error complaints
241  private static long minimumToastWait = 10000000000L; // 10 seconds
242  private long lastToastTime = System.nanoTime() - minimumToastWait;
243 
244  // In a multiple screen application, when a secondary screen is opened, nextFormName is set to
245  // the name of the secondary screen. It is saved so that it can be passed to the OtherScreenClosed
246  // event.
247  private String nextFormName;
248 
249  private FullScreenVideoUtil fullScreenVideoUtil;
250 
251  private int formWidth;
252  private int formHeight;
253 
254  private boolean actionBarEnabled = false;
255  private boolean keyboardShown = false;
256 
257  private ProgressDialog progress;
258  private static boolean _initialized = false;
259 
260  // It should be changed from 100000 to 65535 if the functionality to extend
261  // FragmentActivity is added in future.
262  public static final int MAX_PERMISSION_NONCE = 100000;
263 
264  public static class PercentStorageRecord {
265  public enum Dim {
266  HEIGHT, WIDTH };
267 
268  public PercentStorageRecord(AndroidViewComponent component, int length, Dim dim) {
269  this.component = component;
270  this.length = length;
271  this.dim = dim;
272  }
273 
274  AndroidViewComponent component;
275  int length;
276  Dim dim;
277  }
278  // private ArrayList<PercentStorageRecord> dimChanges = new ArrayList();
279  private LinkedHashMap<Integer, PercentStorageRecord> dimChanges = new LinkedHashMap();
280 
281  private static class MultiDexInstaller extends AsyncTask<Form, Void, Boolean> {
282  Form ourForm;
283 
284  @Override
285  protected Boolean doInBackground(Form... form) {
286  ourForm = form[0];
287  Log.d(LOG_TAG, "Doing Full MultiDex Install");
288  MultiDex.install(ourForm, true); // Force installation
289  return true;
290  }
291  @Override
292  protected void onPostExecute(Boolean v) {
293  ourForm.onCreateFinish();
294  }
295  }
296 
297  @Override
298  public void onCreate(Bundle icicle) {
299  // Called when the activity is first created
300  super.onCreate(icicle);
301 
302  // Figure out the name of this form.
303  String className = getClass().getName();
304  int lastDot = className.lastIndexOf('.');
305  formName = className.substring(lastDot + 1);
306  Log.d(LOG_TAG, "Form " + formName + " got onCreate");
307 
308  activeForm = this;
309  Log.i(LOG_TAG, "activeForm is now " + activeForm.formName);
310 
311  deviceDensity = this.getResources().getDisplayMetrics().density;
312  Log.d(LOG_TAG, "deviceDensity = " + deviceDensity);
313  compatScalingFactor = ScreenDensityUtil.computeCompatibleScaling(this);
314  Log.i(LOG_TAG, "compatScalingFactor = " + compatScalingFactor);
316  alignmentSetter = new AlignmentUtil(viewLayout);
317 
318  progress = null;
319  if (!_initialized && formName.equals("Screen1")) {
320  Log.d(LOG_TAG, "MULTI: _initialized = " + _initialized + " formName = " + formName);
321  _initialized = true;
322  // Note that we always consult ReplApplication even if we are not the Repl (Companion)
323  // this is subtle. When ReplApplication isn't directly used, the "installed" property
324  // defaults to ture, which means we can continue. The MultiDexApplication which is
325  // used in a non-Companion context will always do the full install
327  Log.d(LOG_TAG, "MultiDex already installed.");
328  onCreateFinish();
329  } else {
330  progress = ProgressDialog.show(this, "Please Wait...", "Installation Finishing");
331  progress.show();
332  new MultiDexInstaller().execute(this);
333  }
334  } else {
335  Log.d(LOG_TAG, "NO MULTI: _initialized = " + _initialized + " formName = " + formName);
336  _initialized = true;
337  onCreateFinish();
338  }
339  }
340 
341  /*
342  * Finish the work of setting up the Screen.
343  *
344  * onCreate is done in two parts. The first part is done in onCreate
345  * and the second part is done here. This division is so that we can
346  * asynchronously load classes2.dex if we have to, while displaying
347  * a splash screen which explains that installation is finishing.
348  * We do this because there can be a significant time spent in
349  * DexOpt'ing classes2.dex. Note: If it is already optimized, we
350  * don't show the splash screen and call this function
351  * immediately. Similarly we call this function immediately on any
352  * screen other then Screen1.
353  *
354  */
355 
356  void onCreateFinish() {
357 
358  Log.d(LOG_TAG, "onCreateFinish called " + System.currentTimeMillis());
359  if (progress != null) {
360  progress.dismiss();
361  }
362 
363  populatePermissions();
364 
365  // Check to see if we need to ask for WRITE_EXTERNAL_STORAGE
366  // permission. We look at the application manifest to see if it
367  // is declared there. If it is, then we need to ask the user to
368  // approve it here. Otherwise we don't need to and we can
369  // continue. Because the asking process is asynchronous
370  // we have to have yet another continuation of the onCreate
371  // process (onCreateFinish2). Sigh.
372 
373  boolean needSdcardWrite = doesAppDeclarePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) &&
374  // Only ask permission if we are in the REPL and not using the splash screen
375  isRepl() && !AppInventorFeatures.doCompanionSplashScreen();
376  if (needSdcardWrite) {
377  askPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
378  new PermissionResultHandler() {
379  @Override
380  public void HandlePermissionResponse(String permission, boolean granted) {
381  if (granted) {
382  onCreateFinish2();
383  } else {
384  Log.i(LOG_TAG, "WRITE_EXTERNAL_STORAGE Permission denied by user");
385  onCreateFinish2();
386  androidUIHandler.post(new Runnable() {
387  @Override
388  public void run() {
389  PermissionDenied(Form.this, "Initialize", Manifest.permission.WRITE_EXTERNAL_STORAGE);
390  }
391  });
392  }
393  }
394  });
395  } else {
396  onCreateFinish2();
397  }
398  }
399 
400  private void onCreateFinish2() {
401  defaultPropertyValues();
402 
403  // Get startup text if any before adding components
404  Intent startIntent = getIntent();
405  if (startIntent != null && startIntent.hasExtra(ARGUMENT_NAME)) {
406  startupValue = startIntent.getStringExtra(ARGUMENT_NAME);
407  }
408 
409  fullScreenVideoUtil = new FullScreenVideoUtil(this, androidUIHandler);
410 
411  // Set soft keyboard to not cover the focused UI element, e.g., when you are typing
412  // into a textbox near the bottom of the screen.
413  WindowManager.LayoutParams params = getWindow().getAttributes();
414  int softInputMode = params.softInputMode;
415  getWindow().setSoftInputMode(
416  softInputMode | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
417 
418  // Add application components to the form
419  $define();
420 
421  // Special case for Event.Initialize(): all other initialize events are triggered after
422  // completing the constructor. This doesn't work for Android apps though because this method
423  // is called after the constructor completes and therefore the Initialize event would run
424  // before initialization finishes. Instead the compiler suppresses the invocation of the
425  // event and leaves it up to the library implementation.
426  Initialize();
427  }
428 
432  private void populatePermissions() {
433  try {
434  PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(),
435  PackageManager.GET_PERMISSIONS);
436  Collections.addAll(permissions, packageInfo.requestedPermissions);
437  } catch (Exception e) {
438  Log.e(LOG_TAG, "Exception while attempting to learn permissions.", e);
439  }
440  }
441 
442  private void defaultPropertyValues() {
443  if (isRepl()) {
444  ActionBar(actionBarEnabled);
445  } else {
446  ActionBar(themeHelper.hasActionBar());
447  }
448  Scrollable(false); // frameLayout is created in Scrollable()
449  Sizing("Responsive"); // Note: Only the Screen1 value is used as this is per-project
450  AboutScreen("");
451  BackgroundImage("");
452  AlignHorizontal(ComponentConstants.GRAVITY_LEFT);
453  AlignVertical(ComponentConstants.GRAVITY_TOP);
454  Title("");
455  ShowStatusBar(true);
456  TitleVisible(true);
457  ShowListsAsJson(true); // Note: Only the Screen1 value is used as this is per-project
458  ActionBar(false);
459  AccentColor(DEFAULT_ACCENT_COLOR);
460  PrimaryColor(DEFAULT_PRIMARY_COLOR);
461  PrimaryColorDark(DEFAULT_PRIMARY_COLOR_DARK);
462  Theme(ComponentConstants.DEFAULT_THEME);
463  ScreenOrientation("unspecified");
464  BackgroundColor(Component.COLOR_DEFAULT);
465  }
466 
467  @Override
468  public void onConfigurationChanged(Configuration newConfig) {
469  super.onConfigurationChanged(newConfig);
470  Log.d(LOG_TAG, "onConfigurationChanged() called");
471  final int newOrientation = newConfig.orientation;
472  if (newOrientation == Configuration.ORIENTATION_LANDSCAPE ||
473  newOrientation == Configuration.ORIENTATION_PORTRAIT) {
474  // At this point, the screen has not be resized to match the new orientation.
475  // We use Handler.post so that we'll dispatch the ScreenOrientationChanged event after the
476  // screen has been resized to match the new orientation.
477 
478  androidUIHandler.post(new Runnable() {
479  public void run() {
480  boolean dispatchEventNow = false;
481  if (frameLayout != null) {
482  if (newOrientation == Configuration.ORIENTATION_LANDSCAPE) {
483  if (frameLayout.getWidth() >= frameLayout.getHeight()) {
484  dispatchEventNow = true;
485  }
486  } else { // Portrait
487  if (frameLayout.getHeight() >= frameLayout.getWidth()) {
488  dispatchEventNow = true;
489  }
490  }
491  }
492  if (dispatchEventNow) {
493  recomputeLayout();
494  final FrameLayout savedLayout = frameLayout;
495  androidUIHandler.postDelayed(new Runnable() {
496  public void run() {
497  if (savedLayout != null) {
498  savedLayout.invalidate();
499  }
500  }
501  }, 100); // Redraw the whole screen in 1/10 second
502  // we do this to avoid screen artifacts left
503  // left by the Android runtime.
504  ScreenOrientationChanged();
505  } else {
506  // Try again later.
507  androidUIHandler.post(this);
508  }
509  }
510  });
511  }
512  }
513 
514 // What's this code?
515 //
516 // There is either an App Inventor bug, or Android bug (likely both)
517 // that results in the contents of the screen being rendered "too
518 // tall" on some devices when the soft keyboard is toggled from
519 // displayed to hidden. This results in the bottom of the App being
520 // cut-off. This only happens when we are in "Fixed" mode where we
521 // provide a ScaledFrameLayout whose job is to scale the app to fill
522 // the display of whatever device it is running on ("big phone mode").
523 //
524 // The code below is triggered on every major layout change. It
525 // compares the size of the device window with the height of the
526 // displayed content. Based on the difference, we can tell if the
527 // keyboard is open or closed. We detect the transition from open to
528 // closed and iff we are in "Fixed" mode (sComptabilityMode = true) we
529 // trigger a recomputation of the entire apps layout after a delay of
530 // 100ms (which seems to be required, for reasons we don't quite
531 // understand).
532 //
533 // This code is not really a "fix" but more of a "workaround."
534 
535  @Override
536  public void onGlobalLayout() {
537  int totalHeight = scaleLayout.getRootView().getHeight();
538  int scaledHeight = scaleLayout.getHeight();
539  int heightDiff = totalHeight - scaledHeight;
540  // int[] position = new int[2];
541  // scaleLayout.getLocationInWindow(position);
542  // int contentViewTop = position[1];
543  float diffPercent = (float) heightDiff / (float) totalHeight;
544  Log.d(LOG_TAG, "onGlobalLayout(): diffPercent = " + diffPercent);
545 
546  if(diffPercent < 0.25) { // 0.25 is kind of arbitrary
547  Log.d(LOG_TAG, "keyboard hidden!");
548  if (keyboardShown) {
549  keyboardShown = false;
550  if (sCompatibilityMode) { // Put us back in "Fixed" Mode
551  scaleLayout.setScale(compatScalingFactor);
552  scaleLayout.invalidate();
553  }
554  }
555  } else {
556  Log.d(LOG_TAG, "keyboard shown!");
557  keyboardShown = true;
558  if (scaleLayout != null) { // Effectively put us in responsive mode
559  scaleLayout.setScale(1.0f);
560  scaleLayout.invalidate();
561  }
562  }
563  }
564 
565  /*
566  * Here we override the hardware back button, just to make sure
567  * that the closing screen animation is applied.
568  */
569  @Override
570  public void onBackPressed() {
571  if (!BackPressed()) {
572  AnimationUtil.ApplyCloseScreenAnimation(this, closeAnimType);
573  super.onBackPressed();
574  }
575  }
576 
577  @SimpleEvent(description = "Device back button pressed.")
578  public boolean BackPressed() {
579  return EventDispatcher.dispatchEvent(this, "BackPressed");
580  }
581 
582  // onActivityResult should be triggered in only two cases:
583  // (1) The result is for some other component in the app, not this Form itself
584  // (2) This page started another page, and that page is closing, and passing
585  // its value back as a JSON-encoded string in the intent.
586 
587  @Override
588  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
589  Log.i(LOG_TAG, "Form " + formName + " got onActivityResult, requestCode = " +
590  requestCode + ", resultCode = " + resultCode);
591  if (requestCode == SWITCH_FORM_REQUEST_CODE) {
592  // Assume this is a multiple screen application, and a secondary
593  // screen has closed. Process the result as a JSON-encoded string.
594  // This can also happen if the user presses the back button, in which case
595  // there's no data.
596  String resultString;
597  if (data != null && data.hasExtra(RESULT_NAME)) {
598  resultString = data.getStringExtra(RESULT_NAME);
599  } else {
600  resultString = "";
601  }
602  Object decodedResult = decodeJSONStringForForm(resultString, "other screen closed");
603  // nextFormName was set when this screen opened the secondary screen
604  OtherScreenClosed(nextFormName, decodedResult);
605  } else {
606  // Another component (such as a ListPicker, ActivityStarter, etc) is expecting this result.
607  ActivityResultListener component = activityResultMap.get(requestCode);
608  if (component != null) {
609  component.resultReturned(requestCode, resultCode, data);
610  }
611  // Many components are interested in this request (e.g., Texting, PhoneCall)
612  Set<ActivityResultListener> listeners = activityResultMultiMap.get(requestCode);
613  if (listeners != null) {
614  for (ActivityResultListener listener : listeners.toArray(new ActivityResultListener[0])) {
615  listener.resultReturned(requestCode, resultCode, data);
616  }
617  }
618  }
619  }
620 
621  // functionName is a string to include in the error message that will be shown
622  // if the JSON decoding fails
623  private static Object decodeJSONStringForForm(String jsonString, String functionName) {
624  Log.i(LOG_TAG, "decodeJSONStringForForm -- decoding JSON representation:" + jsonString);
625  Object valueFromJSON = "";
626  try {
627  valueFromJSON = JsonUtil.getObjectFromJson(jsonString, true);
628  Log.i(LOG_TAG, "decodeJSONStringForForm -- got decoded JSON:" + valueFromJSON.toString());
629  } catch (JSONException e) {
630  activeForm.dispatchErrorOccurredEvent(activeForm, functionName,
631  // showing the start value here will produce an ugly error on the phone, but it's
632  // more useful than not showing the value
633  ErrorMessages.ERROR_SCREEN_BAD_VALUE_RECEIVED, jsonString);
634  }
635  return valueFromJSON;
636  }
637 
639  int requestCode = generateNewRequestCode();
640  activityResultMap.put(requestCode, listener);
641  return requestCode;
642  }
643 
651  public void registerForActivityResult(ActivityResultListener listener, int requestCode) {
652  Set<ActivityResultListener> listeners = activityResultMultiMap.get(requestCode);
653  if (listeners == null) {
654  listeners = Sets.newHashSet();
655  activityResultMultiMap.put(requestCode, listeners);
656  }
657  listeners.add(listener);
658  }
659 
661  List<Integer> keysToDelete = Lists.newArrayList();
662  for (Map.Entry<Integer, ActivityResultListener> mapEntry : activityResultMap.entrySet()) {
663  if (listener.equals(mapEntry.getValue())) {
664  keysToDelete.add(mapEntry.getKey());
665  }
666  }
667  for (Integer key : keysToDelete) {
668  activityResultMap.remove(key);
669  }
670 
671  // Remove any simulated broadcast receivers
672  Iterator<Map.Entry<Integer, Set<ActivityResultListener>>> it =
673  activityResultMultiMap.entrySet().iterator();
674  while (it.hasNext()) {
675  Map.Entry<Integer, Set<ActivityResultListener>> entry = it.next();
676  entry.getValue().remove(listener);
677  if (entry.getValue().size() == 0) {
678  it.remove();
679  }
680  }
681  }
682 
683  void ReplayFormOrientation() {
684  // We first make a copy of the existing dimChanges list
685  // because while we are replaying it, it is being appended to
686  Log.d(LOG_TAG, "ReplayFormOrientation()");
687  LinkedHashMap<Integer, PercentStorageRecord> temp = (LinkedHashMap<Integer, PercentStorageRecord>) dimChanges.clone();
688  dimChanges.clear(); // Empties it out
689  // Iterate temp
690  for (PercentStorageRecord r : temp.values()) {
691  if (r.dim == PercentStorageRecord.Dim.HEIGHT) {
692  r.component.Height(r.length);
693  } else {
694  r.component.Width(r.length);
695  }
696  }
697  }
698 
699  private Integer generateHashCode(AndroidViewComponent component, PercentStorageRecord.Dim dim) {
700  if (dim == PercentStorageRecord.Dim.HEIGHT) {
701  return component.hashCode() * 2 + 1;
702  } else {
703  return component.hashCode() * 2;
704  }
705  }
706 
707  public void registerPercentLength(AndroidViewComponent component, int length, PercentStorageRecord.Dim dim) {
708  PercentStorageRecord r = new PercentStorageRecord(component, length, dim);
709  Integer key = generateHashCode(component, dim);
710  dimChanges.put(key, r);
711  }
712 
713  public void unregisterPercentLength(AndroidViewComponent component, PercentStorageRecord.Dim dim) {
714  // iterate map, remove all entry match this
715  dimChanges.remove(generateHashCode(component, dim));
716  }
717 
718  private static int generateNewRequestCode() {
719  return nextRequestCode++;
720  }
721 
722  @Override
723  protected void onResume() {
724  super.onResume();
725  Log.i(LOG_TAG, "Form " + formName + " got onResume");
726  activeForm = this;
727 
728  // If applicationIsBeingClosed is true, call closeApplication() immediately to continue
729  // unwinding through all forms of a multi-screen application.
730  if (applicationIsBeingClosed) {
731  closeApplication();
732  return;
733  }
734 
735  for (OnResumeListener onResumeListener : onResumeListeners) {
736  onResumeListener.onResume();
737  }
738  }
739 
740  public void registerForOnResume(OnResumeListener component) {
741  onResumeListeners.add(component);
742  }
743 
745  onOrientationChangeListeners.add(component);
746  }
747 
755  onInitializeListeners.add(component);
756  }
757 
758  @Override
759  protected void onNewIntent(Intent intent) {
760  super.onNewIntent(intent);
761  Log.d(LOG_TAG, "Form " + formName + " got onNewIntent " + intent);
762  for (OnNewIntentListener onNewIntentListener : onNewIntentListeners) {
763  onNewIntentListener.onNewIntent(intent);
764  }
765  }
766 
768  onNewIntentListeners.add(component);
769  }
770 
771  @Override
772  protected void onPause() {
773  super.onPause();
774  Log.i(LOG_TAG, "Form " + formName + " got onPause");
775  for (OnPauseListener onPauseListener : onPauseListeners) {
776  onPauseListener.onPause();
777  }
778  }
779 
780  public void registerForOnPause(OnPauseListener component) {
781  onPauseListeners.add(component);
782  }
783 
784  @Override
785  protected void onStop() {
786  super.onStop();
787  Log.i(LOG_TAG, "Form " + formName + " got onStop");
788  for (OnStopListener onStopListener : onStopListeners) {
789  onStopListener.onStop();
790  }
791  }
792 
793  public void registerForOnStop(OnStopListener component) {
794  onStopListeners.add(component);
795  }
796 
797  public void registerForOnClear(OnClearListener component) {
798  onClearListeners.add(component);
799  }
800 
801  @Override
802  protected void onDestroy() {
803  // for debugging and future growth
804  Log.i(LOG_TAG, "Form " + formName + " got onDestroy");
805 
806  // Unregister events for components in this form.
808 
809  for (OnDestroyListener onDestroyListener : onDestroyListeners) {
810  onDestroyListener.onDestroy();
811  }
812 
813  // call super method at the end to delegate the destruction of the app to the parent
814  super.onDestroy();
815  }
816 
817  public void registerForOnDestroy(OnDestroyListener component) {
818  onDestroyListeners.add(component);
819  }
820 
822  onCreateOptionsMenuListeners.add(component);
823  }
824 
826  onOptionsItemSelectedListeners.add(component);
827  }
828 
829  public Dialog onCreateDialog(int id) {
830  switch(id) {
832  return fullScreenVideoUtil.createFullScreenVideoDialog();
833  default:
834  return super.onCreateDialog(id);
835  }
836  }
837 
838  public void onPrepareDialog(int id, Dialog dialog) {
839  switch(id) {
841  fullScreenVideoUtil.prepareFullScreenVideoDialog(dialog);
842  break;
843  default:
844  super.onPrepareDialog(id, dialog);
845  }
846  }
847 
857  protected void $define() { // This must be declared protected because we are called from Screen1 which subclasses
858  // us and isn't in our package.
859  throw new UnsupportedOperationException();
860  }
861 
862  @Override
863  public boolean canDispatchEvent(Component component, String eventName) {
864  // Events can only be dispatched after the screen initialized event has completed.
865  boolean canDispatch = screenInitialized ||
866  (component == this && eventName.equals("Initialize"));
867 
868  if (canDispatch) {
869  // Set activeForm to this before the event is dispatched.
870  // runtime.scm will call getActiveForm() when the event handler executes.
871  activeForm = this;
872  }
873 
874  return canDispatch;
875  }
876 
885  @Override
886  public boolean dispatchEvent(Component component, String componentName, String eventName,
887  Object[] args) {
888  throw new UnsupportedOperationException();
889  }
890 
891  @Override
892  public void dispatchGenericEvent(Component component, String eventName,
893  boolean notAlreadyHandled, Object[] args) {
894  throw new UnsupportedOperationException();
895  }
896 
897  @SimpleEvent(description = "The Initialize event is run when the Screen starts and is only run "
898  + "once per screen.")
899  public void Initialize() {
900  // Dispatch the Initialize event only after the screen's width and height are no longer zero.
901  androidUIHandler.post(new Runnable() {
902  public void run() {
903  if (frameLayout != null && frameLayout.getWidth() != 0 && frameLayout.getHeight() != 0) {
904  EventDispatcher.dispatchEvent(Form.this, "Initialize");
905  if (sCompatibilityMode) { // Make sure call to setLayout happens
906  Sizing("Fixed");
907  } else {
908  Sizing("Responsive");
909  }
910  screenInitialized = true;
911 
912  // Call all apps registered to be notified when Initialize Event is dispatched
913  for (OnInitializeListener onInitializeListener : onInitializeListeners) {
914  onInitializeListener.onInitialize();
915  }
916  if (activeForm instanceof ReplForm) { // We are the Companion
917  ((ReplForm)activeForm).HandleReturnValues();
918  }
919  } else {
920  // Try again later.
921  androidUIHandler.post(this);
922  }
923  }
924  });
925  }
926 
927  @SimpleEvent(description = "Screen orientation changed")
928  public void ScreenOrientationChanged() {
929  for (OnOrientationChangeListener onOrientationChangeListener : onOrientationChangeListeners) {
930  onOrientationChangeListener.onOrientationChange();
931  }
932  EventDispatcher.dispatchEvent(this, "ScreenOrientationChanged");
933  }
934 
935  @SimpleEvent(
936  description = "Event raised when an error occurs. Only some errors will " +
937  "raise this condition. For those errors, the system will show a notification " +
938  "by default. You can use this event handler to prescribe an error " +
939  "behavior different than the default.")
940  public void ErrorOccurred(Component component, String functionName, int errorNumber,
941  String message) {
942  String componentType = component.getClass().getName();
943  componentType = componentType.substring(componentType.lastIndexOf(".") + 1);
944  Log.e(LOG_TAG, "Form " + formName + " ErrorOccurred, errorNumber = " + errorNumber +
945  ", componentType = " + componentType + ", functionName = " + functionName +
946  ", messages = " + message);
948  this, "ErrorOccurred", component, functionName, errorNumber, message)))
949  && screenInitialized) {
950  // If dispatchEvent returned false, then no user-supplied error handler was run.
951  // If in addition, the screen initializer was run, then we assume that the
952  // user did not provide an error handler. In this case, we run a default
953  // error handler, namely, showing a notification to the end user of the app.
954  // The app writer can override this by providing an error handler.
955  new Notifier(this).ShowAlert("Error " + errorNumber + ": " + message);
956  }
957  }
958 
959 
960  public void ErrorOccurredDialog(Component component, String functionName, int errorNumber,
961  String message, String title, String buttonText) {
962  String componentType = component.getClass().getName();
963  componentType = componentType.substring(componentType.lastIndexOf(".") + 1);
964  Log.e(LOG_TAG, "Form " + formName + " ErrorOccurred, errorNumber = " + errorNumber +
965  ", componentType = " + componentType + ", functionName = " + functionName +
966  ", messages = " + message);
968  this, "ErrorOccurred", component, functionName, errorNumber, message)))
969  && screenInitialized) {
970  // If dispatchEvent returned false, then no user-supplied error handler was run.
971  // If in addition, the screen initializer was run, then we assume that the
972  // user did not provide an error handler. In this case, we run a default
973  // error handler, namely, showing a message dialog to the end user of the app.
974  // The app writer can override this by providing an error handler.
975  new Notifier(this).ShowMessageDialog("Error " + errorNumber + ": " + message, title, buttonText);
976  }
977  }
978 
987  public void dispatchPermissionDeniedEvent(final Component component, final String functionName,
988  final PermissionException exception) {
989  exception.printStackTrace();
990  dispatchPermissionDeniedEvent(component, functionName, exception.getPermissionNeeded());
991  }
992 
1001  public void dispatchPermissionDeniedEvent(final Component component, final String functionName,
1002  final String permissionName) {
1003  runOnUiThread(new Runnable() {
1004  @Override
1005  public void run() {
1006  PermissionDenied(component, functionName, permissionName);
1007  }
1008  });
1009  }
1010 
1011  public void dispatchErrorOccurredEvent(final Component component, final String functionName,
1012  final int errorNumber, final Object... messageArgs) {
1013  runOnUiThread(new Runnable() {
1014  public void run() {
1015  String message = ErrorMessages.formatMessage(errorNumber, messageArgs);
1016  ErrorOccurred(component, functionName, errorNumber, message);
1017  }
1018  });
1019  }
1020 
1021  // This is like dispatchErrorOccurredEvent, except that it defaults to showing
1022  // a message dialog rather than an alert. The app writer can override either of these behaviors,
1023  // but using the event dialog version frees the app writer from the need to explicitly override
1024  // the alert behavior in the case
1025  // where a message dialog is what's generally needed.
1026  public void dispatchErrorOccurredEventDialog(final Component component, final String functionName,
1027  final int errorNumber, final Object... messageArgs) {
1028  runOnUiThread(new Runnable() {
1029  public void run() {
1030  String message = ErrorMessages.formatMessage(errorNumber, messageArgs);
1031  ErrorOccurredDialog(
1032  component,
1033  functionName,
1034  errorNumber,
1035  message,
1036  "Error in " + functionName,
1037  "Dismiss");
1038  }
1039  });
1040  }
1041 
1042  // This runtimeFormErrorOccurred can be called from runtime.scm in
1043  // the case of a runtime error. The event is always signaled in the
1044  // active form. It triggers the normal Form error system which fires
1045  // the ErrorOccurred event. This can be handled by the App Inventor
1046  // programmer. If it isn't a Notifier (toast) is displayed showing
1047  // the error.
1048  public void runtimeFormErrorOccurredEvent(String functionName, int errorNumber, String message) {
1049  Log.d("FORM_RUNTIME_ERROR", "functionName is " + functionName);
1050  Log.d("FORM_RUNTIME_ERROR", "errorNumber is " + errorNumber);
1051  Log.d("FORM_RUNTIME_ERROR", "message is " + message);
1052  dispatchErrorOccurredEvent((Component) activeForm, functionName, errorNumber, message);
1053  }
1054 
1062  @SimpleEvent
1063  public void PermissionDenied(Component component, String functionName, String permissionName) {
1064  if (permissionName.startsWith("android.permission.")) {
1065  // Forward compatibility with iOS so that we don't have to pass around Android-specific names
1066  permissionName = permissionName.replace("android.permission.", "");
1067  }
1068  if (!EventDispatcher.dispatchEvent(this, "PermissionDenied", component, functionName, permissionName)) {
1069  dispatchErrorOccurredEvent(component, functionName, ErrorMessages.ERROR_PERMISSION_DENIED, permissionName);
1070  }
1071  }
1072 
1079  @SimpleEvent(description = "Event to handle when the app user has granted a needed permission. "
1080  + "This event is only run when permission is granted in response to the AskForPermission "
1081  + "method.")
1082  public void PermissionGranted(String permissionName) {
1083  if (permissionName.startsWith("android.permission.")) {
1084  // Forward compatibility with iOS so that we don't have to pass around Android-specific names
1085  permissionName = permissionName.replace("android.permission.", "");
1086  }
1087  EventDispatcher.dispatchEvent(this, "PermissionGranted", permissionName);
1088  }
1089 
1104  @SimpleFunction(description = "Ask the user to grant access to a dangerous permission.")
1105  public void AskForPermission(String permissionName) {
1106  if (!permissionName.contains(".")) {
1107  permissionName = "android.permission." + permissionName;
1108  }
1109  askPermission(permissionName, new PermissionResultHandler() {
1110  @Override
1111  public void HandlePermissionResponse(String permission, boolean granted) {
1112  if (granted) {
1113  PermissionGranted(permission);
1114  } else {
1115  PermissionDenied(Form.this, "RequestPermission", permission);
1116  }
1117  }
1118  });
1119  }
1120 
1127  description = "When checked, there will be a vertical scrollbar on the "
1128  + "screen, and the height of the application can exceed the physical "
1129  + "height of the device. When unchecked, the application height is "
1130  + "constrained to the height of the device.")
1131  public boolean Scrollable() {
1132  return scrollable;
1133  }
1134 
1143  defaultValue = "False")
1145  public void Scrollable(boolean scrollable) {
1146  if (this.scrollable == scrollable && frameLayout != null) {
1147  return;
1148  }
1149 
1150  this.scrollable = scrollable;
1151  recomputeLayout();
1152  }
1153 
1154  private void recomputeLayout() {
1155 
1156  Log.d(LOG_TAG, "recomputeLayout called");
1157  // Remove our view from the current frameLayout.
1158  if (frameLayout != null) {
1159  frameLayout.removeAllViews();
1160  }
1161  boolean needsTitleBar = titleBar != null && titleBar.getParent() == frameWithTitle;
1162  frameWithTitle.removeAllViews();
1163  if (needsTitleBar) {
1164  frameWithTitle.addView(titleBar, new ViewGroup.LayoutParams(
1165  ViewGroup.LayoutParams.MATCH_PARENT,
1166  ViewGroup.LayoutParams.WRAP_CONTENT
1167  ));
1168  }
1169 
1170  // Layout
1171  // ------frameWithTitle------
1172  // | [======titleBar======] |
1173  // | ------scaleLayout----- |
1174  // | | ----frameLayout--- | |
1175  // | | | | | |
1176  // | | ------------------ | |
1177  // | ---------------------- |
1178  // --------------------------
1179 
1180  if (scrollable) {
1181  frameLayout = new ScrollView(this);
1182  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
1183  // Nougat changes how ScrollView handles its content size. Here we force it to fill the viewport
1184  // in order to preserve the layout of apps developed prior to N that rely on the old behavior.
1185  ((ScrollView) frameLayout).setFillViewport(true);
1186  }
1187  } else {
1188  frameLayout = new FrameLayout(this);
1189  }
1190  frameLayout.addView(viewLayout.getLayoutManager(), new ViewGroup.LayoutParams(
1191  ViewGroup.LayoutParams.MATCH_PARENT,
1192  ViewGroup.LayoutParams.MATCH_PARENT));
1193 
1194  setBackground(frameLayout);
1195 
1196  Log.d(LOG_TAG, "About to create a new ScaledFrameLayout");
1197  scaleLayout = new ScaledFrameLayout(this);
1198  scaleLayout.addView(frameLayout, new ViewGroup.LayoutParams(
1199  ViewGroup.LayoutParams.MATCH_PARENT,
1200  ViewGroup.LayoutParams.MATCH_PARENT));
1201  frameWithTitle.addView(scaleLayout, new ViewGroup.LayoutParams(
1202  ViewGroup.LayoutParams.MATCH_PARENT,
1203  ViewGroup.LayoutParams.MATCH_PARENT));
1204  frameLayout.getViewTreeObserver().addOnGlobalLayoutListener(this);
1205  scaleLayout.requestLayout();
1206  androidUIHandler.post(new Runnable() {
1207  public void run() {
1208  if (frameLayout != null && frameLayout.getWidth() != 0 && frameLayout.getHeight() != 0) {
1209  if (sCompatibilityMode) { // Make sure call to setLayout happens
1210  Sizing("Fixed");
1211  } else {
1212  Sizing("Responsive");
1213  }
1214  ReplayFormOrientation(); // Re-do Form layout because percentage code
1215  // needs to recompute objects sizes etc.
1216  frameWithTitle.requestLayout();
1217  } else {
1218  // Try again later.
1219  androidUIHandler.post(this);
1220  }
1221  }
1222  });
1223  }
1224 
1230  @SimpleProperty(category = PropertyCategory.APPEARANCE)
1231  @IsColor
1232  public int BackgroundColor() {
1233  return backgroundColor;
1234  }
1235 
1244  defaultValue = Component.DEFAULT_VALUE_COLOR_WHITE)
1246  public void BackgroundColor(int argb) {
1247  if (argb == Component.COLOR_DEFAULT) {
1248  usesDefaultBackground = true;
1249  } else {
1250  usesDefaultBackground = false;
1251  backgroundColor = argb;
1252  }
1253  // setBackground(viewLayout.getLayoutManager()); // Doesn't seem necessary anymore
1254  setBackground(frameLayout);
1255  }
1256 
1262  @SimpleProperty(
1263  category = PropertyCategory.APPEARANCE,
1264  description = "The screen background image.")
1265  public String BackgroundImage() {
1266  return backgroundImagePath;
1267  }
1268 
1269 
1281  defaultValue = "")
1282  @SimpleProperty(
1283  category = PropertyCategory.APPEARANCE,
1284  description = "The screen background image.")
1285  public void BackgroundImage(String path) {
1286  backgroundImagePath = (path == null) ? "" : path;
1287 
1288  try {
1289  backgroundDrawable = MediaUtil.getBitmapDrawable(this, backgroundImagePath);
1290  } catch (IOException ioe) {
1291  Log.e(LOG_TAG, "Unable to load " + backgroundImagePath);
1292  backgroundDrawable = null;
1293  }
1294  setBackground(frameLayout);
1295  }
1296 
1303  description = "The caption for the form, which apears in the title bar")
1304  public String Title() {
1305  return getTitle().toString();
1306  }
1307 
1315  defaultValue = "")
1317  public void Title(String title) {
1318  this.title = title;
1319  if (titleBar != null) {
1320  titleBar.setText(title);
1321  }
1322  setTitle(title);
1323  updateTitle();
1324  }
1325 
1326 
1333  description = "Information about the screen. It appears when \"About this Application\" "
1334  + "is selected from the system menu. Use it to inform people about your app. In multiple "
1335  + "screen apps, each screen has its own AboutScreen info.")
1336  public String AboutScreen() {
1337  return aboutScreen;
1338  }
1339 
1348  defaultValue = "")
1350  public void AboutScreen(String aboutScreen) {
1351  this.aboutScreen = aboutScreen;
1352  }
1353 
1360  description = "The title bar is the top gray bar on the screen. This property reports whether the title bar is visible.")
1361  public boolean TitleVisible() {
1362  return showTitle;
1363  }
1364 
1372  defaultValue = "True")
1374  public void TitleVisible(boolean show) {
1375  if (show != showTitle) {
1376  showTitle = show;
1377  if (actionBarEnabled) {
1378  actionBarEnabled = themeHelper.setActionBarVisible(show);
1379  } else {
1380  maybeShowTitleBar();
1381  }
1382  }
1383  }
1384 
1391  description = "The status bar is the topmost bar on the screen. This property reports whether the status bar is visible.")
1392  public boolean ShowStatusBar() {
1393  return showStatusBar;
1394  }
1395 
1403  defaultValue = "True")
1405  public void ShowStatusBar(boolean show) {
1406  if (show != showStatusBar) {
1407  if (show) {
1408  getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
1409  getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1410  } else {
1411  getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
1412  getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
1413  }
1414  showStatusBar = show;
1415  }
1416  }
1417 
1429  description = "The requested screen orientation, specified as a text value. " +
1430  "Commonly used values are " +
1431  "landscape, portrait, sensor, user and unspecified. " +
1432  "See the Android developer documentation for ActivityInfo.Screen_Orientation for the " +
1433  "complete list of possible settings.")
1434  public String ScreenOrientation() {
1435  switch (getRequestedOrientation()) {
1436  case ActivityInfo.SCREEN_ORIENTATION_BEHIND:
1437  return "behind";
1438  case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
1439  return "landscape";
1440  case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR:
1441  return "nosensor";
1442  case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
1443  return "portrait";
1444  case ActivityInfo.SCREEN_ORIENTATION_SENSOR:
1445  return "sensor";
1446  case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
1447  return "unspecified";
1448  case ActivityInfo.SCREEN_ORIENTATION_USER:
1449  return "user";
1450  case 10: // ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1451  return "fullSensor";
1452  case 8: // ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1453  return "reverseLandscape";
1454  case 9: // ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
1455  return "reversePortrait";
1456  case 6: // ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1457  return "sensorLandscape";
1458  case 7: // ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
1459  return "sensorPortrait";
1460  }
1461 
1462  return "unspecified";
1463  }
1464 
1473  @SuppressLint("SourceLockedOrientationActivity")
1475  defaultValue = "unspecified")
1477  public void ScreenOrientation(String screenOrientation) {
1478  if (screenOrientation.equalsIgnoreCase("behind")) {
1479  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
1480  } else if (screenOrientation.equalsIgnoreCase("landscape")) {
1481  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
1482  } else if (screenOrientation.equalsIgnoreCase("nosensor")) {
1483  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
1484  } else if (screenOrientation.equalsIgnoreCase("portrait")) {
1485  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
1486  } else if (screenOrientation.equalsIgnoreCase("sensor")) {
1487  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
1488  } else if (screenOrientation.equalsIgnoreCase("unspecified")) {
1489  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1490  } else if (screenOrientation.equalsIgnoreCase("user")) {
1491  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
1492  } else if (SdkLevel.getLevel() >= SdkLevel.LEVEL_GINGERBREAD) {
1493  if (screenOrientation.equalsIgnoreCase("fullSensor")) {
1494  setRequestedOrientation(10); // ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
1495  } else if (screenOrientation.equalsIgnoreCase("reverseLandscape")) {
1496  setRequestedOrientation(8); // ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
1497  } else if (screenOrientation.equalsIgnoreCase("reversePortrait")) {
1498  setRequestedOrientation(9); // ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
1499  } else if (screenOrientation.equalsIgnoreCase("sensorLandscape")) {
1500  setRequestedOrientation(6); // ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
1501  } else if (screenOrientation.equalsIgnoreCase("sensorPortrait")) {
1502  setRequestedOrientation(7); // ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
1503  } else {
1504  dispatchErrorOccurredEvent(this, "ScreenOrientation",
1505  ErrorMessages.ERROR_INVALID_SCREEN_ORIENTATION, screenOrientation);
1506  }
1507  } else {
1508  dispatchErrorOccurredEvent(this, "ScreenOrientation",
1509  ErrorMessages.ERROR_INVALID_SCREEN_ORIENTATION, screenOrientation);
1510  }
1511  }
1512 
1514  defaultValue = "False")
1515  @SimpleProperty(userVisible = false)
1516  public void ActionBar(boolean enabled) {
1518  // ActionBar is available on SDK 11 or higher
1519  return;
1520  }
1521  if (actionBarEnabled != enabled) {
1522  setActionBarEnabled(enabled);
1523  if (enabled) {
1524  hideTitleBar();
1525  actionBarEnabled = themeHelper.setActionBarVisible(showTitle);
1526  } else {
1527  maybeShowTitleBar();
1528  actionBarEnabled = themeHelper.setActionBarVisible(false);
1529  }
1530  actionBarEnabled = enabled;
1531  }
1532  }
1533 
1534  // Note(halabelson): This section on centering is duplicated between Form and HVArrangement
1535  // I did not see a clean way to abstract it. Someone should have a look.
1536 
1537  // Note(halabelson): The numeric encodings of the alignment specifications are specified
1538  // in ComponentConstants
1539 
1544  @SimpleProperty(
1545  category = PropertyCategory.APPEARANCE,
1546  description = "A number that encodes how contents of the screen are aligned " +
1547  " horizontally. The choices are: 1 = left aligned, 2 = horizontally centered, " +
1548  " 3 = right aligned.")
1549  public int AlignHorizontal() {
1550  return horizontalAlignment;
1551  }
1552 
1565  public void AlignHorizontal(int alignment) {
1566  try {
1567  // notice that the throw will prevent the alignment from being changed
1568  // if the argument is illegal
1569  alignmentSetter.setHorizontalAlignment(alignment);
1570  horizontalAlignment = alignment;
1571  } catch (IllegalArgumentException e) {
1572  this.dispatchErrorOccurredEvent(this, "HorizontalAlignment",
1574  }
1575  }
1576 
1582  @SimpleProperty(
1583  category = PropertyCategory.APPEARANCE,
1584  description = "A number that encodes how the contents of the arrangement are aligned " +
1585  "vertically. The choices are: 1 = aligned at the top, 2 = vertically centered, " +
1586  "3 = aligned at the bottom. Vertical alignment has no effect if the screen is scrollable.")
1587  public int AlignVertical() {
1588  return verticalAlignment;
1589  }
1590 
1602  defaultValue = ComponentConstants.VERTICAL_ALIGNMENT_DEFAULT + "")
1604  public void AlignVertical(int alignment) {
1605  try {
1606  // notice that the throw will prevent the alignment from being changed
1607  // if the argument is illegal
1608  alignmentSetter.setVerticalAlignment(alignment);
1609  verticalAlignment = alignment;
1610  } catch (IllegalArgumentException e) {
1611  this.dispatchErrorOccurredEvent(this, "VerticalAlignment",
1613  }
1614  }
1615 
1623  description = "The animation for switching to another screen. Valid" +
1624  " options are default, fade, zoom, slidehorizontal, slidevertical, and none" )
1625  public String OpenScreenAnimation() {
1626  return openAnimType;
1627  }
1628 
1636  defaultValue = "default")
1638  public void OpenScreenAnimation(String animType) {
1639  if ((animType != "default") &&
1640  (animType != "fade") && (animType != "zoom") && (animType != "slidehorizontal") &&
1641  (animType != "slidevertical") && (animType != "none")) {
1642  this.dispatchErrorOccurredEvent(this, "Screen",
1644  return;
1645  }
1646  openAnimType = animType;
1647  }
1648 
1656  description = "The animation for closing current screen and returning " +
1657  " to the previous screen. Valid options are default, fade, zoom, slidehorizontal, " +
1658  "slidevertical, and none")
1659  public String CloseScreenAnimation() {
1660  return closeAnimType;
1661  }
1662 
1670  defaultValue = "default")
1672  public void CloseScreenAnimation(String animType) {
1673  if ((animType != "default") &&
1674  (animType != "fade") && (animType != "zoom") && (animType != "slidehorizontal") &&
1675  (animType != "slidevertical") && (animType != "none")) {
1676  this.dispatchErrorOccurredEvent(this, "Screen",
1678  return;
1679  }
1680  closeAnimType = animType;
1681  }
1682 
1683  /*
1684  * Used by ListPicker, and ActivityStarter to get this Form's current opening transition
1685  * animation
1686  */
1687  public String getOpenAnimType() {
1688  return openAnimType;
1689  }
1690 
1699  defaultValue = "")
1700  @SimpleProperty(userVisible = false)
1701  public void Icon(String name) {
1702  // We don't actually need to do anything.
1703  }
1704 
1712  defaultValue = "1")
1713  @SimpleProperty(userVisible = false,
1714  description = "An integer value which must be incremented each time a new Android "
1715  + "Application Package File (APK) is created for the Google Play Store.")
1716  public void VersionCode(int vCode) {
1717  // We don't actually need to do anything.
1718  }
1719 
1727  defaultValue = "1.0")
1728  @SimpleProperty(userVisible = false,
1729  description = "A string which can be changed to allow Google Play "
1730  + "Store users to distinguish between different versions of the App.")
1731  public void VersionName(String vName) {
1732  // We don't actually need to do anything.
1733  }
1734 
1745  defaultValue = "Responsive", alwaysSend = true)
1746  @SimpleProperty(userVisible = false,
1747  // This desc won't apprear as a tooltip, since there's no block, but we'll keep it with the source.
1748  description = "If set to fixed, screen layouts will be created for a single fixed-size screen and autoscaled. " +
1749  "If set to responsive, screen layouts will use the actual resolution of the device. " +
1750  "See the documentation on responsive design in App Inventor for more information. " +
1751  "This property appears on Screen1 only and controls the sizing for all screens in the app.")
1752  public void Sizing(String value) {
1753  // This is used by the project and build server.
1754  // We also use it to adjust sizes
1755  Log.d(LOG_TAG, "Sizing(" + value + ")");
1756  formWidth = (int)((float) this.getResources().getDisplayMetrics().widthPixels / deviceDensity);
1757  formHeight = (int)((float) this.getResources().getDisplayMetrics().heightPixels / deviceDensity);
1758  if (value.equals("Fixed")) {
1759  sCompatibilityMode = true;
1760  formWidth /= compatScalingFactor;
1761  formHeight /= compatScalingFactor;
1762  } else {
1763  sCompatibilityMode = false;
1764  }
1765  scaleLayout.setScale(sCompatibilityMode ? compatScalingFactor : 1.0f);
1766  if (frameLayout != null) {
1767  frameLayout.invalidate();
1768  }
1769  Log.d(LOG_TAG, "formWidth = " + formWidth + " formHeight = " + formHeight);
1770  }
1771 
1772  // public String Sizing() {
1773  // if (compatibilityMode) {
1774  // return "Fixed";
1775  // } else {
1776  // return "Responsive";
1777  // }
1778  // }
1779 
1787  defaultValue = "True", alwaysSend = true)
1788  @SimpleProperty(category = PropertyCategory.APPEARANCE, userVisible = false,
1789  // This description won't appear as a tooltip, since there's no block, but we'll keep it with the source.
1790  description = "If false, lists will be converted to strings using Lisp "
1791  + "notation, i.e., as symbols separated by spaces, e.g., (a 1 b2 (c "
1792  + "d). If true, lists will appear as in Json or Python, e.g. [\"a\", 1, "
1793  + "\"b\", 2, [\"c\", \"d\"]]. This property appears only in Screen 1, "
1794  + "and the value for Screen 1 determines the behavior for all "
1795  + "screens. The property defaults to \"true\" meaning that the App "
1796  + "Inventor programmer must explicitly set it to \"false\" if Lisp "
1797  + "syntax is desired. In older versions of App Inventor, this setting "
1798  + "defaulted to false. Older projects should not have been affected by "
1799  + "this default settings update."
1800  )
1801  public void ShowListsAsJson(boolean asJson) {
1802  showListsAsJson = asJson;
1803  }
1804 
1813  @SimpleProperty(category = PropertyCategory.APPEARANCE, userVisible = false)
1814  public boolean ShowListsAsJson() {
1815  return showListsAsJson;
1816  }
1817 
1825  defaultValue = "")
1826  @SimpleProperty(userVisible = false,
1827  description = "This is the display name of the installed application in the phone." +
1828  "If the AppName is blank, it will be set to the name of the project when the project is built.")
1829  public void AppName(String aName) {
1830  // We don't actually need to do anything.
1831  }
1832 
1835  @SimpleProperty(userVisible = false, description = "This is the primary color used for " +
1836  "Material UI elements, such as the ActionBar.", category = PropertyCategory.APPEARANCE)
1837  public void PrimaryColor(final int color) {
1838  setPrimaryColor(color);
1839  }
1840 
1846  @IsColor
1847  public int PrimaryColor() {
1848  return primaryColor;
1849  }
1850 
1853  @SimpleProperty(userVisible = false, description = "This is the primary color used for darker " +
1854  "elements in Material UI.", category = PropertyCategory.APPEARANCE)
1855  public void PrimaryColorDark(int color) {
1856  primaryColorDark = color;
1857  }
1858 
1863  @SimpleProperty()
1864  @IsColor
1865  public int PrimaryColorDark() {
1866  return primaryColorDark;
1867  }
1868 
1871  @SimpleProperty(userVisible = false, description = "This is the accent color used for " +
1872  "highlights and other user interface accents.", category = PropertyCategory.APPEARANCE)
1873  public void AccentColor(int color) {
1874  accentColor = color;
1875  }
1876 
1883  @IsColor
1884  public int AccentColor() {
1885  return accentColor;
1886  }
1887 
1900  defaultValue = ComponentConstants.DEFAULT_THEME)
1901  @SimpleProperty(userVisible = false, description = "Sets the theme used by the application.")
1902  public void Theme(String theme) {
1904  backgroundColor = Component.COLOR_WHITE;
1905  setBackground(frameLayout);
1906  return; // Only "Classic" is supported below SDK 11 due to minSDK in AppCompat
1907  }
1908  if (usesDefaultBackground) {
1909  if (theme.equalsIgnoreCase("AppTheme") && !isClassicMode()) {
1910  backgroundColor = Component.COLOR_BLACK;
1911  } else {
1912  backgroundColor = Component.COLOR_WHITE;
1913  }
1914  setBackground(frameLayout);
1915  }
1916  usesDarkTheme = false;
1917  if (theme.equals("Classic")) {
1918  setAppInventorTheme(Theme.CLASSIC);
1919  } else if (theme.equals("AppTheme.Light.DarkActionBar")) {
1920  setAppInventorTheme(Theme.DEVICE_DEFAULT);
1921  } else if (theme.equals("AppTheme.Light")) {
1922  setAppInventorTheme(Theme.BLACK_TITLE_TEXT);
1923  } else if (theme.equals("AppTheme")) {
1924  usesDarkTheme = true;
1925  setAppInventorTheme(Theme.DARK);
1926  }
1927  }
1928 
1935  description = "Screen width (x-size).")
1936  public int Width() {
1937  Log.d(LOG_TAG, "Form.Width = " + formWidth);
1938  return formWidth;
1939  }
1940 
1947  description = "Screen height (y-size).")
1948  public int Height() {
1949  Log.d(LOG_TAG, "Form.Height = " + formHeight);
1950  return formHeight;
1951  }
1952 
1960  defaultValue = "")
1961  @SimpleProperty(userVisible = false,
1962  description = "A URL to use to populate the Tutorial Sidebar while "
1963  + "editing a project. Used as a teaching aid.")
1964  public void TutorialURL(String url) {
1965  // We don't actually do anything This property is stored in the
1966  // project properties file
1967  }
1968 
1970  defaultValue = "")
1971  @SimpleProperty(userVisible = false,
1972  description = "A JSON string representing the subset for the screen. Authors of template apps "
1973  + "can use this to control what components, designer properties, and blocks are available "
1974  + "in the project.")
1975  public void BlocksToolkit(String json) {
1976  // We don't actually do anything. This property is stored in the
1977  // project properties file
1978  }
1979 
1986  @SimpleProperty(description = "The platform the app is running on, for example \"Android\" or "
1987  + "\"iOS\".")
1988  public String Platform() {
1989  return "Android";
1990  }
1991 
1999  @SimpleProperty(description = "The dotted version number of the platform, such as 4.2.2 or 10.0. "
2000  + "This is platform specific and there is no guarantee that it has a particular format.")
2001  public String PlatformVersion() {
2002  return Build.VERSION.RELEASE;
2003  }
2004 
2010  // This is called from runtime.scm when a "open another screen" block is executed.
2011  public static void switchForm(String nextFormName) {
2012  if (activeForm != null) {
2013  activeForm.startNewForm(nextFormName, null);
2014  } else {
2015  throw new IllegalStateException("activeForm is null");
2016  }
2017  }
2018 
2025  // This is called from runtime.scm when a "open another screen with start value" block is
2026  // executed. Note that startNewForm will JSON encode the start value
2027  public static void switchFormWithStartValue(String nextFormName, Object startValue) {
2028  Log.i(LOG_TAG, "Open another screen with start value:" + nextFormName);
2029  if (activeForm != null) {
2030  activeForm.startNewForm(nextFormName, startValue);
2031  } else {
2032  throw new IllegalStateException("activeForm is null");
2033  }
2034  }
2035 
2036  // This JSON encodes the startup value
2037  protected void startNewForm(String nextFormName, Object startupValue) {
2038  Log.i(LOG_TAG, "startNewForm:" + nextFormName);
2039  Intent activityIntent = new Intent();
2040  // Note that the following is dependent on form generated class names being the same as
2041  // their form names and all forms being in the same package.
2042  activityIntent.setClassName(this, getPackageName() + "." + nextFormName);
2043  String functionName = (startupValue == null) ? "open another screen" :
2044  "open another screen with start value";
2045  String jValue;
2046  if (startupValue != null) {
2047  Log.i(LOG_TAG, "StartNewForm about to JSON encode:" + startupValue);
2048  jValue = jsonEncodeForForm(startupValue, functionName);
2049  Log.i(LOG_TAG, "StartNewForm got JSON encoding:" + jValue);
2050  } else{
2051  jValue = "";
2052  }
2053  activityIntent.putExtra(ARGUMENT_NAME, jValue);
2054  // Save the nextFormName so that it can be passed to the OtherScreenClosed event in the
2055  // future.
2056  this.nextFormName = nextFormName;
2057  Log.i(LOG_TAG, "about to start new form" + nextFormName);
2058  try {
2059  Log.i(LOG_TAG, "startNewForm starting activity:" + activityIntent);
2060  startActivityForResult(activityIntent, SWITCH_FORM_REQUEST_CODE);
2061  AnimationUtil.ApplyOpenScreenAnimation(this, openAnimType);
2062  } catch (ActivityNotFoundException e) {
2063  dispatchErrorOccurredEvent(this, functionName,
2064  ErrorMessages.ERROR_SCREEN_NOT_FOUND, nextFormName);
2065  }
2066  }
2067 
2068  // functionName is used for including in the error message to be shown
2069  // if the JSON encoding fails
2070  protected static String jsonEncodeForForm(Object value, String functionName) {
2071  String jsonResult = "";
2072  Log.i(LOG_TAG, "jsonEncodeForForm -- creating JSON representation:" + value.toString());
2073  try {
2074  // TODO(hal): check that this is OK for raw strings
2075  jsonResult = JsonUtil.getJsonRepresentation(value);
2076  Log.i(LOG_TAG, "jsonEncodeForForm -- got JSON representation:" + jsonResult);
2077  } catch (JSONException e) {
2078  activeForm.dispatchErrorOccurredEvent(activeForm, functionName,
2079  // showing the bad value here will produce an ugly error on the phone, but it's
2080  // more useful than not showing the value
2082  }
2083  return jsonResult;
2084  }
2085 
2086  @SimpleEvent(description = "Event raised when another screen has closed and control has " +
2087  "returned to this screen.")
2088  public void OtherScreenClosed(String otherScreenName, Object result) {
2089  Log.i(LOG_TAG, "Form " + formName + " OtherScreenClosed, otherScreenName = " +
2090  otherScreenName + ", result = " + result.toString());
2091  EventDispatcher.dispatchEvent(this, "OtherScreenClosed", otherScreenName, result);
2092  }
2093 
2094 
2095  // Component implementation
2096 
2097  @Override
2099  return this;
2100  }
2101 
2102  // ComponentContainer implementation
2103 
2104  @Override
2105  public Activity $context() {
2106  return this;
2107  }
2108 
2109  @Override
2110  public Form $form() {
2111  return this;
2112  }
2113 
2114  @Override
2115  public void $add(AndroidViewComponent component) {
2116  viewLayout.add(component);
2117  }
2118 
2119  public float deviceDensity(){
2120  return this.deviceDensity;
2121  }
2122 
2123  public float compatScalingFactor() {
2124  return this.compatScalingFactor;
2125  }
2126 
2127  @Override
2128  public void setChildWidth(final AndroidViewComponent component, int width) {
2129  int cWidth = Width();
2130  if (cWidth == 0) { // We're not really ready yet...
2131  final int fWidth = width;
2132  androidUIHandler.postDelayed(new Runnable() {
2133  @Override
2134  public void run() {
2135  System.err.println("(Form)Width not stable yet... trying again");
2136  setChildWidth(component, fWidth);
2137  }
2138  }, 100); // Try again in 1/10 of a second
2139  }
2140  System.err.println("Form.setChildWidth(): width = " + width + " parent Width = " + cWidth + " child = " + component);
2141  if (width <= LENGTH_PERCENT_TAG) {
2142  width = cWidth * (- (width - LENGTH_PERCENT_TAG)) / 100;
2143 // System.err.println("Form.setChildWidth(): Setting " + component + " lastwidth to " + width);
2144  }
2145 
2146  component.setLastWidth(width);
2147 
2148  // A form is a vertical layout.
2149  ViewUtil.setChildWidthForVerticalLayout(component.getView(), width);
2150  }
2151 
2152  @Override
2153  public void setChildHeight(final AndroidViewComponent component, int height) {
2154  int cHeight = Height();
2155  if (cHeight == 0) { // Not ready yet...
2156  final int fHeight = height;
2157  androidUIHandler.postDelayed(new Runnable() {
2158  @Override
2159  public void run() {
2160  System.err.println("(Form)Height not stable yet... trying again");
2161  setChildHeight(component, fHeight);
2162  }
2163  }, 100); // Try again in 1/10 of a second
2164  }
2165  if (height <= LENGTH_PERCENT_TAG) {
2166  height = Height() * (- (height - LENGTH_PERCENT_TAG)) / 100;
2167  }
2168 
2169  component.setLastHeight(height);
2170 
2171  // A form is a vertical layout.
2172  ViewUtil.setChildHeightForVerticalLayout(component.getView(), height);
2173  }
2174 
2175  /*
2176  * This is called from runtime.scm at the beginning of each event handler.
2177  * It allows runtime.scm to know which form environment should be used for
2178  * looking up symbols. The active form is the form that is currently
2179  * (or was most recently) dispatching an event.
2180  */
2181  public static Form getActiveForm() {
2182  return activeForm;
2183  }
2184 
2185 
2191  // This is called from runtime.scm when a "get plain start text" block is executed.
2192  public static String getStartText() {
2193  if (activeForm != null) {
2194  return activeForm.startupValue;
2195  } else {
2196  throw new IllegalStateException("activeForm is null");
2197  }
2198  }
2199 
2200 
2206  // TODO(hal): cache this?
2207  // Note: This is called as a primitive from runtime.scm and it returns an arbitrary Java object.
2208  // Therefore it must be explicitly sanitized by runtime, unlike methods, which
2209  // are sanitized via call-component-method.
2210  public static Object getStartValue() {
2211  if (activeForm != null) {
2212  return decodeJSONStringForForm(activeForm.startupValue, "get start value");
2213  } else {
2214  throw new IllegalStateException("activeForm is null");
2215  }
2216  }
2217 
2218 
2223  // This is called from runtime.scm when a "close screen" block is executed.
2224  public static void finishActivity() {
2225  if (activeForm != null) {
2226  activeForm.closeForm(null);
2227  } else {
2228  throw new IllegalStateException("activeForm is null");
2229  }
2230  }
2231 
2232  // This is called from runtime.scm when a "close screen with value" block is executed.
2233  public static void finishActivityWithResult(Object result) {
2234  if (activeForm != null) {
2235  if (activeForm instanceof ReplForm) {
2236  ((ReplForm)activeForm).setResult(result);
2237  activeForm.closeForm(null); // This will call RetValManager.popScreen()
2238  } else {
2239  String jString = jsonEncodeForForm(result, "close screen with value");
2240  Intent resultIntent = new Intent();
2241  resultIntent.putExtra(RESULT_NAME, jString);
2242  activeForm.closeForm(resultIntent);
2243  }
2244  } else {
2245  throw new IllegalStateException("activeForm is null");
2246  }
2247  }
2248 
2249  // This is called from runtime.scm when a "close screen with plain text" block is executed.
2250  public static void finishActivityWithTextResult(String result) {
2251  if (activeForm != null) {
2252  Intent resultIntent = new Intent();
2253  resultIntent.putExtra(RESULT_NAME, result);
2254  activeForm.closeForm(resultIntent);
2255  } else {
2256  throw new IllegalStateException("activeForm is null");
2257  }
2258  }
2259 
2260 
2261  protected void closeForm(Intent resultIntent) {
2262  if (resultIntent != null) {
2263  setResult(Activity.RESULT_OK, resultIntent);
2264  }
2265  finish();
2266  AnimationUtil.ApplyCloseScreenAnimation(this, closeAnimType);
2267  }
2268 
2269  // This is called from runtime.scm when a "close application" block is executed.
2270  public static void finishApplication() {
2271  if (activeForm != null) {
2272  activeForm.closeApplicationFromBlocks();
2273  } else {
2274  throw new IllegalStateException("activeForm is null");
2275  }
2276  }
2277 
2278  protected void closeApplicationFromBlocks() {
2279  closeApplication();
2280  }
2281 
2282  private void closeApplicationFromMenu() {
2283  closeApplication();
2284  }
2285 
2286  private void closeApplication() {
2287  // In a multi-screen application, only Screen1 can successfully call System.exit(0). Here, we
2288  // set applicationIsBeingClosed to true. If this is not Screen1, when we call finish() below,
2289  // the previous form's onResume method will be called. In onResume, we check
2290  // applicationIsBeingClosed and call closeApplication again. The stack of forms will unwind
2291  // until we get back to Screen1; then we'll call System.exit(0) below.
2292  applicationIsBeingClosed = true;
2293 
2294  finish();
2295 
2296  if (formName.equals("Screen1")) {
2297  // I know that this is frowned upon in Android circles but I really think that it's
2298  // confusing to users if the exit button doesn't really stop everything, including other
2299  // forms in the app (when we support them), non-UI threads, etc. We might need to be
2300  // careful about this is we ever support services that start up on boot (since it might
2301  // mean that the only way to restart that service) is to reboot but that's a long way off.
2302  System.exit(0);
2303  }
2304  }
2305 
2306  // Configure the system menu to include items to kill the application and to show "about"
2307  // information
2308 
2309  @Override
2310  public boolean onCreateOptionsMenu(Menu menu) {
2311  // This procedure is called only once. To change the items dynamically
2312  // we would use onPrepareOptionsMenu.
2313  super.onCreateOptionsMenu(menu);
2314  // add the menu items
2315  // Comment out the next line if we don't want the exit button
2316  addExitButtonToMenu(menu);
2317  addAboutInfoToMenu(menu);
2318  for (OnCreateOptionsMenuListener onCreateOptionsMenuListener : onCreateOptionsMenuListeners) {
2319  onCreateOptionsMenuListener.onCreateOptionsMenu(menu);
2320  }
2321  return true;
2322  }
2323 
2324  public void addExitButtonToMenu(Menu menu) {
2325  MenuItem stopApplicationItem = menu.add(Menu.NONE, Menu.NONE, Menu.FIRST,
2326  "Stop this application")
2327  .setOnMenuItemClickListener(new OnMenuItemClickListener() {
2328  public boolean onMenuItemClick(MenuItem item) {
2329  showExitApplicationNotification();
2330  return true;
2331  }
2332  });
2333  stopApplicationItem.setIcon(android.R.drawable.ic_notification_clear_all);
2334  }
2335 
2336  public void addAboutInfoToMenu(Menu menu) {
2337  MenuItem aboutAppItem = menu.add(Menu.NONE, Menu.NONE, 2,
2338  "About this application")
2339  .setOnMenuItemClickListener(new OnMenuItemClickListener() {
2340  public boolean onMenuItemClick(MenuItem item) {
2341  showAboutApplicationNotification();
2342  return true;
2343  }
2344  });
2345  aboutAppItem.setIcon(android.R.drawable.sym_def_app_icon);
2346  }
2347 
2348  @Override
2349  public boolean onOptionsItemSelected(MenuItem item) {
2350  for (OnOptionsItemSelectedListener onOptionsItemSelectedListener : onOptionsItemSelectedListeners) {
2351  if (onOptionsItemSelectedListener.onOptionsItemSelected(item)) {
2352  return true;
2353  }
2354  }
2355  return false;
2356  }
2357 
2358  private void showExitApplicationNotification() {
2359  String title = "Stop application?";
2360  String message = "Stop this application and exit? You'll need to relaunch " +
2361  "the application to use it again.";
2362  String positiveButton = "Stop and exit";
2363  String negativeButton = "Don't stop";
2364  // These runnables are passed to twoButtonAlert. They perform the corresponding actions
2365  // when the button is pressed. Here there's nothing to do for "don't stop" and cancel
2366  Runnable stopApplication = new Runnable() {public void run () {closeApplicationFromMenu();}};
2367  Runnable doNothing = new Runnable () {public void run() {}};
2368  Notifier.twoButtonDialog(
2369  this,
2370  message,
2371  title,
2372  positiveButton,
2373  negativeButton,
2374  false, // cancelable is false
2375  stopApplication,
2376  doNothing,
2377  doNothing);
2378  }
2379 
2380  private String yandexTranslateTagline = "";
2381 
2382  void setYandexTranslateTagline(){
2383  yandexTranslateTagline = "<p><small>Language translation powered by Yandex.Translate</small></p>";
2384  }
2385 
2386  private void showAboutApplicationNotification() {
2387  String title = "About this app";
2388  String MITtagline = "<p><small><em>Invented with MIT App Inventor<br>appinventor.mit.edu</em></small></p>";
2389  // Users can hide the taglines by including an HTML open comment <!-- in the about screen message
2390  String message = aboutScreen + MITtagline + yandexTranslateTagline;
2391  message = message.replaceAll("\\n", "<br>"); // Allow for line breaks in the string.
2392  String buttonText ="Got it";
2393  Notifier.oneButtonAlert(this, message, title, buttonText);
2394  }
2395 
2396  // This is called from clear-current-form in runtime.scm.
2397  public void clear() {
2398  Log.d(LOG_TAG, "Form " + formName + " clear called");
2399  viewLayout.getLayoutManager().removeAllViews();
2400  if (frameLayout != null) {
2401  frameLayout.removeAllViews();
2402  frameLayout = null;
2403  }
2404  // Set all screen properties to default values.
2405  defaultPropertyValues();
2406  onStopListeners.clear();
2407  onNewIntentListeners.clear();
2408  onResumeListeners.clear();
2409  onOrientationChangeListeners.clear();
2410  onPauseListeners.clear();
2411  onDestroyListeners.clear();
2412  onInitializeListeners.clear();
2413  onCreateOptionsMenuListeners.clear();
2414  onOptionsItemSelectedListeners.clear();
2415  screenInitialized = false;
2416  // Notifiy those who care
2417  for (OnClearListener onClearListener : onClearListeners) {
2418  onClearListener.onClear();
2419  }
2420  // And reset the list
2421  onClearListeners.clear();
2422  System.err.println("Form.clear() About to do moby GC!");
2423  System.gc();
2424  dimChanges.clear();
2425  }
2426 
2427  public void deleteComponent(Object component) {
2428  if (component instanceof OnStopListener) {
2429  onStopListeners.remove(component);
2430  }
2431  if (component instanceof OnNewIntentListener) {
2432  onNewIntentListeners.remove(component);
2433  }
2434  if (component instanceof OnResumeListener) {
2435  onResumeListeners.remove(component);
2436  }
2437  if (component instanceof OnOrientationChangeListener) {
2438  onOrientationChangeListeners.remove(component);
2439  }
2440  if (component instanceof OnPauseListener) {
2441  onPauseListeners.remove(component);
2442  }
2443  if (component instanceof OnDestroyListener) {
2444  onDestroyListeners.remove(component);
2445  }
2446  if (component instanceof OnInitializeListener) {
2447  onInitializeListeners.remove(component);
2448  }
2449  if (component instanceof OnCreateOptionsMenuListener) {
2450  onCreateOptionsMenuListeners.remove(component);
2451  }
2452  if (component instanceof OnOptionsItemSelectedListener) {
2453  onOptionsItemSelectedListeners.remove(component);
2454  }
2455  if (component instanceof Deleteable) {
2456  ((Deleteable) component).onDelete();
2457  }
2458  }
2459 
2461  // The following call results in the Form not grabbing our events and
2462  // handling dragging on its own, which it wants to do to handle scrolling.
2463  // Its effect only lasts long as the current set of motion events
2464  // generated during this touch and drag sequence. Consequently, if a
2465  // component wants to handle dragging it needs to call this in the
2466  // onTouchEvent of its View.
2467  frameLayout.requestDisallowInterceptTouchEvent(true);
2468  }
2469 
2470 
2471  // This is used by Repl to throttle error messages which can get out of
2472  // hand, e.g. if triggered by Accelerometer.
2473  protected boolean toastAllowed() {
2474  long now = System.nanoTime();
2475  if (now > lastToastTime + minimumToastWait) {
2476  lastToastTime = now;
2477  return true;
2478  }
2479  return false;
2480  }
2481 
2482  // This is used by runtime.scm to call the Initialize of a component.
2483  public void callInitialize(Object component) throws Throwable {
2484  Method method;
2485  try {
2486  method = component.getClass().getMethod("Initialize", (Class<?>[]) null);
2487  } catch (SecurityException e) {
2488  Log.i(LOG_TAG, "Security exception " + e.getMessage());
2489  return;
2490  } catch (NoSuchMethodException e) {
2491  //This is OK.
2492  return;
2493  }
2494  try {
2495  Log.i(LOG_TAG, "calling Initialize method for Object " + component.toString());
2496  method.invoke(component, (Object[]) null);
2497  } catch (InvocationTargetException e){
2498  Log.i(LOG_TAG, "invoke exception: " + e.getMessage());
2499  throw e.getTargetException();
2500  }
2501  }
2502 
2536  public synchronized Bundle fullScreenVideoAction(int action, VideoPlayer source, Object data) {
2537  return fullScreenVideoUtil.performAction(action, source, data);
2538  }
2539 
2540  private void setBackground(View bgview) {
2541  Drawable setDraw = backgroundDrawable;
2542  if (backgroundImagePath != "" && setDraw != null) {
2543  setDraw = backgroundDrawable.getConstantState().newDrawable();
2544  setDraw.setColorFilter((backgroundColor != Component.COLOR_DEFAULT) ? backgroundColor : Component.COLOR_WHITE,
2545  PorterDuff.Mode.DST_OVER);
2546  } else {
2547  setDraw = new ColorDrawable(
2548  (backgroundColor != Component.COLOR_DEFAULT) ? backgroundColor : Component.COLOR_WHITE);
2549  }
2550  ViewUtil.setBackgroundImage(bgview, setDraw);
2551  bgview.invalidate();
2552  }
2553 
2554  public static boolean getCompatibilityMode() {
2555  return sCompatibilityMode;
2556  }
2557 
2561  @SimpleFunction(description = "Hide the onscreen soft keyboard.")
2562  public void HideKeyboard() {
2563  View view = this.getCurrentFocus();
2564  if (view == null) {
2565  view = frameLayout;
2566  }
2567  InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
2568  imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
2569  }
2570 
2571  protected void updateTitle() {
2572  themeHelper.setTitle(title);
2573  }
2574 
2575  @Override
2576  protected void maybeShowTitleBar() {
2577  if (showTitle) {
2578  super.maybeShowTitleBar();
2579  } else {
2580  super.hideTitleBar();
2581  }
2582  }
2583 
2584  public boolean isDarkTheme() {
2585  return usesDarkTheme;
2586  }
2587 
2588  // Permission Handling Code
2589 
2596  public boolean isDeniedPermission(String permission) {
2597  return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
2598  ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_DENIED;
2599  }
2600 
2607  public void assertPermission(String permission) {
2608  if (isDeniedPermission(permission)) {
2609  throw new PermissionException(permission);
2610  }
2611  }
2612 
2633  public void askPermission(final String permission, final PermissionResultHandler responseRequestor) {
2634  final Form form = this;
2635  if (!isDeniedPermission(permission)) {
2636  // We already have permission, so no need to ask
2637  responseRequestor.HandlePermissionResponse(permission, true);
2638  return;
2639  }
2640  androidUIHandler.post(new Runnable() {
2641  @Override
2642  public void run() {
2643  int nonce = permissionRandom.nextInt(MAX_PERMISSION_NONCE);
2644  Log.d(LOG_TAG, "askPermission: permission = " + permission +
2645  " requestCode = " + nonce);
2646  permissionHandlers.put(nonce, responseRequestor);
2647  ActivityCompat.requestPermissions((Activity)form,
2648  new String[] {permission}, nonce);
2649  }
2650  });
2651  }
2652 
2658  public void askPermission(final BulkPermissionRequest request) {
2659  final List<String> permissionsToAsk = request.getPermissions();
2660  Iterator<String> it = permissionsToAsk.iterator();
2661  while (it.hasNext()) {
2662  if (!isDeniedPermission(it.next())) {
2663  it.remove();
2664  }
2665  }
2666  if (permissionsToAsk.size() == 0) {
2667  // We already have all the necessary permissions
2668  request.onGranted();
2669  } else {
2670  // Make sure we ask for permissions on the UI thread
2671  androidUIHandler.post(new Runnable() {
2672  @Override
2673  public void run() {
2674  final Iterator<String> it = permissionsToAsk.iterator();
2675  final PermissionResultHandler handler = new PermissionResultHandler() {
2676  final List<String> deniedPermissions = new ArrayList<String>();
2677 
2678  @Override
2679  public void HandlePermissionResponse(String permission, boolean granted) {
2680  if (!granted) {
2681  deniedPermissions.add(permission);
2682  }
2683  if (it.hasNext()) {
2684  askPermission(it.next(), this);
2685  } else {
2686  if (deniedPermissions.size() == 0) {
2687  request.onGranted();
2688  } else {
2689  request.onDenied(deniedPermissions.toArray(new String[] {}));
2690  }
2691  }
2692  }
2693  };
2694  askPermission(it.next(), handler);
2695  }
2696  });
2697  }
2698  }
2699 
2700  @Override
2701  public void onRequestPermissionsResult(int requestCode,
2702  String permissions[], int[] grantResults) {
2703  PermissionResultHandler responder = permissionHandlers.get(requestCode);
2704  if (responder == null) {
2705  // Hmm. Shouldn't happen
2706  Log.e(LOG_TAG, "Received permission response which we cannot match.");
2707  return;
2708  }
2709  if (grantResults.length > 0) {
2710  if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
2711  responder.HandlePermissionResponse(permissions[0], true);
2712  } else {
2713  responder.HandlePermissionResponse(permissions[0], false);
2714  }
2715  } else {
2716  Log.d(LOG_TAG, "onRequestPermissionsResult: grantResults.length = " + grantResults.length +
2717  " requestCode = " + requestCode);
2718  }
2719  permissionHandlers.remove(requestCode);
2720  }
2721 
2729  @SuppressWarnings("WeakerAccess") // May be used by extensions
2730  public boolean doesAppDeclarePermission(String permissionName) {
2731  return permissions.contains(permissionName);
2732  }
2733 
2740  public String getAssetPath(String asset) {
2741  return ASSETS_PREFIX + asset;
2742  }
2743 
2751  @SuppressWarnings({"WeakerAccess"}) // May be called by extensions
2752  public InputStream openAsset(String asset) throws IOException {
2753  return openAssetInternal(getAssetPath(asset));
2754  }
2755 
2765  public String getAssetPathForExtension(Component component, String asset) throws FileNotFoundException {
2766  String extPkgName = component.getClass().getPackage().getName();
2767  return ASSETS_PREFIX + extPkgName + "/" + asset;
2768  }
2769 
2780  @SuppressWarnings("unused") // May be called by extensions
2781  public InputStream openAssetForExtension(Component component, String asset) throws IOException {
2782  return openAssetInternal(getAssetPathForExtension(component, asset));
2783  }
2784 
2785  @SuppressWarnings("WeakerAccess") // Visible for testing
2786  @VisibleForTesting
2787  InputStream openAssetInternal(String path) throws IOException {
2788  if (path.startsWith(ASSETS_PREFIX)) {
2789  final AssetManager am = getAssets();
2790  return am.open(path.substring(ASSETS_PREFIX.length()));
2791  } else if (path.startsWith("file:")) {
2792  return FileUtil.openFile(this, URI.create(path));
2793  } else {
2794  return FileUtil.openFile(this, path);
2795  }
2796  }
2797 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_GINGERBREAD
static final int LEVEL_GINGERBREAD
Definition: SdkLevel.java:26
com.google.appinventor.components.runtime.util.MediaUtil.getBitmapDrawable
static BitmapDrawable getBitmapDrawable(Form form, String mediaPath)
Definition: MediaUtil.java:419
com.google.appinventor.components.runtime.ReplForm
Definition: ReplForm.java:62
com.google.appinventor.components.runtime.Form.fullScreenVideoAction
synchronized Bundle fullScreenVideoAction(int action, VideoPlayer source, Object data)
Definition: Form.java:2536
com.google.appinventor.components.runtime.Form.askPermission
void askPermission(final String permission, final PermissionResultHandler responseRequestor)
Definition: Form.java:2633
com.google.appinventor.components.runtime.util.ErrorMessages.formatMessage
static String formatMessage(int errorNumber, Object[] messageArgs)
Definition: ErrorMessages.java:699
com.google.appinventor.components.runtime.multidex.MultiDex
Definition: MultiDex.java:57
com.google.appinventor.components.runtime.AndroidViewComponent.setLastHeight
void setLastHeight(int height)
Definition: AndroidViewComponent.java:143
com.google.appinventor.components.runtime.Form.onDestroy
void onDestroy()
Definition: Form.java:802
com.google.appinventor.components.runtime.Form.dispatchGenericEvent
void dispatchGenericEvent(Component component, String eventName, boolean notAlreadyHandled, Object[] args)
Definition: Form.java:892
com.google.appinventor.components.runtime.multidex
Definition: MultiDex.java:17
com.google.appinventor.components.runtime.Component.COLOR_DEFAULT
static final int COLOR_DEFAULT
Definition: Component.java:68
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.AppInventorCompatActivity.Theme.DARK
DARK
Definition: AppInventorCompatActivity.java:47
com.google.appinventor.components.common.ComponentConstants.DEFAULT_PRIMARY_DARK_COLOR
static final String DEFAULT_PRIMARY_DARK_COLOR
Definition: ComponentConstants.java:89
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_SCREEN_BAD_VALUE_FOR_SENDING
static final int ERROR_SCREEN_BAD_VALUE_FOR_SENDING
Definition: ErrorMessages.java:116
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_BAD_VALUE_FOR_VERTICAL_ALIGNMENT
static final int ERROR_BAD_VALUE_FOR_VERTICAL_ALIGNMENT
Definition: ErrorMessages.java:155
com.google.appinventor.components.runtime.ReplApplication.installed
static boolean installed
Definition: ReplApplication.java:44
com.google.appinventor.components.runtime.HandlesEventDispatching
Definition: HandlesEventDispatching.java:15
com.google.appinventor.components.runtime.Form.registerForOnDestroy
void registerForOnDestroy(OnDestroyListener component)
Definition: Form.java:817
com.google.appinventor.components.runtime.util.ErrorMessages
Definition: ErrorMessages.java:17
com.google.appinventor.components.runtime.Form.registerForOnInitialize
void registerForOnInitialize(OnInitializeListener component)
Definition: Form.java:754
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.Form.PercentStorageRecord.Dim
Definition: Form.java:265
com.google.appinventor.components.runtime.util.PaintUtil
Definition: PaintUtil.java:17
com.google.appinventor.components.runtime.Form.startupValue
String startupValue
Definition: Form.java:238
com.google.appinventor.components.runtime.util.FileUtil
Definition: FileUtil.java:37
com.google.appinventor.components.runtime.Form.PrimaryColorDark
int PrimaryColorDark()
Definition: Form.java:1865
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.runtime.Form.getDispatchDelegate
HandlesEventDispatching getDispatchDelegate()
Definition: Form.java:2098
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.runtime.util.FullScreenVideoUtil
Definition: FullScreenVideoUtil.java:39
com.google.appinventor.components.runtime.Form.switchForm
static void switchForm(String nextFormName)
Definition: Form.java:2011
com.google.appinventor.components.runtime.AndroidViewComponent.Height
int Height()
Definition: AndroidViewComponent.java:178
com.google.appinventor.components.common.ComponentConstants.DEFAULT_THEME
static final String DEFAULT_THEME
Definition: ComponentConstants.java:87
com.google.appinventor.components.runtime.Form.registerForOnResume
void registerForOnResume(OnResumeListener component)
Definition: Form.java:740
com.google.appinventor.components.runtime.Form.formName
String formName
Definition: Form.java:162
com.google.appinventor.components.runtime.ActivityResultListener.resultReturned
void resultReturned(int requestCode, int resultCode, Intent data)
com.google.appinventor.components.runtime.Form.getStartValue
static Object getStartValue()
Definition: Form.java:2210
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components.runtime.util.BulkPermissionRequest.onDenied
void onDenied(String[] permissions)
Definition: BulkPermissionRequest.java:53
com.google.appinventor.components.runtime.LinearLayout.add
void add(AndroidViewComponent component)
Definition: LinearLayout.java:108
com.google.appinventor.components.runtime.PermissionResultHandler.HandlePermissionResponse
void HandlePermissionResponse(String permission, boolean granted)
com.google.appinventor.components
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_INTEGER
static final String PROPERTY_TYPE_NON_NEGATIVE_INTEGER
Definition: PropertyTypeConstants.java:206
com.google.appinventor.components.runtime.Component.DEFAULT_VALUE_COLOR_WHITE
static final String DEFAULT_VALUE_COLOR_WHITE
Definition: Component.java:82
com.google.appinventor.components.runtime.Form.dispatchEvent
boolean dispatchEvent(Component component, String componentName, String eventName, Object[] args)
Definition: Form.java:886
com.google.appinventor.components.runtime.Form.PermissionDenied
void PermissionDenied(Component component, String functionName, String permissionName)
Definition: Form.java:1063
com.google.appinventor.components.runtime.util.JsonUtil
Definition: JsonUtil.java:42
com.google.appinventor.components.runtime.Form.registerForOnOrientationChange
void registerForOnOrientationChange(OnOrientationChangeListener component)
Definition: Form.java:744
com.google.appinventor.components.runtime.Form.callInitialize
void callInitialize(Object component)
Definition: Form.java:2483
com.google.appinventor.components.runtime.Form.$add
void $add(AndroidViewComponent component)
Definition: Form.java:2115
com.google.appinventor.components.runtime.Form.startNewForm
void startNewForm(String nextFormName, Object startupValue)
Definition: Form.java:2037
com.google.appinventor.components.runtime.Form.activeForm
static Form activeForm
Definition: Form.java:152
com.google.appinventor.components.runtime.Form.closeApplicationFromBlocks
void closeApplicationFromBlocks()
Definition: Form.java:2278
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN
static final String PROPERTY_TYPE_BOOLEAN
Definition: PropertyTypeConstants.java:35
com.google.appinventor.components.runtime.Form.onOptionsItemSelected
boolean onOptionsItemSelected(MenuItem item)
Definition: Form.java:2349
com.google.appinventor.components.runtime.Form.getStartText
static String getStartText()
Definition: Form.java:2192
com.google.appinventor.components.runtime.util.AnimationUtil
Definition: AnimationUtil.java:24
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_SCREEN_INVALID_ANIMATION
static final int ERROR_SCREEN_INVALID_ANIMATION
Definition: ErrorMessages.java:117
com.google.appinventor.components.runtime.util.MediaUtil
Definition: MediaUtil.java:53
com.google.appinventor.components.runtime.Form.OpenScreenAnimation
void OpenScreenAnimation(String animType)
Definition: Form.java:1638
com.google.appinventor.components.runtime.Form.PrimaryColor
int PrimaryColor()
Definition: Form.java:1847
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_SCREEN_ANIMATION
static final String PROPERTY_TYPE_SCREEN_ANIMATION
Definition: PropertyTypeConstants.java:218
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.runtime.Form.askPermission
void askPermission(final BulkPermissionRequest request)
Definition: Form.java:2658
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.runtime.Form.$define
void $define()
Definition: Form.java:857
com.google.appinventor.components.runtime.util.FullScreenVideoUtil.createFullScreenVideoDialog
Dialog createFullScreenVideoDialog()
Definition: FullScreenVideoUtil.java:283
com.google.appinventor.components.runtime.Form.onNewIntent
void onNewIntent(Intent intent)
Definition: Form.java:759
com.google.appinventor.components.runtime.util.AlignmentUtil.setHorizontalAlignment
void setHorizontalAlignment(int alignment)
Definition: AlignmentUtil.java:30
com.google.appinventor.components.runtime.AndroidViewComponent.setLastWidth
void setLastWidth(int width)
Definition: AndroidViewComponent.java:129
com.google.appinventor.components.runtime.Form.closeForm
void closeForm(Intent resultIntent)
Definition: Form.java:2261
com.google.appinventor.components.runtime.collect
Definition: Lists.java:7
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_SIZING
static final String PROPERTY_TYPE_SIZING
Definition: PropertyTypeConstants.java:303
com.google.appinventor.components.runtime.Notifier.ShowAlert
void ShowAlert(final String notice)
Definition: Notifier.java:435
com.google.appinventor.components.runtime.OnNewIntentListener
Definition: OnNewIntentListener.java:13
com.google.appinventor.components.runtime.Form.dispatchPermissionDeniedEvent
void dispatchPermissionDeniedEvent(final Component component, final String functionName, final PermissionException exception)
Definition: Form.java:987
com.google.appinventor.components.runtime.collect.Sets.newHashSet
static< K > HashSet< K > newHashSet()
Definition: Sets.java:36
com.google.appinventor.components.runtime.collect.Lists.newArrayList
static< E > ArrayList< E > newArrayList()
Definition: Lists.java:30
com.google.appinventor.components.runtime.Form.onPause
void onPause()
Definition: Form.java:772
com.google.appinventor.components.runtime.Form.jsonEncodeForForm
static String jsonEncodeForForm(Object value, String functionName)
Definition: Form.java:2070
com.google.appinventor.components.runtime.Form.getAssetPath
String getAssetPath(String asset)
Definition: Form.java:2740
com.google.appinventor.components.runtime.Form.onCreate
void onCreate(Bundle icicle)
Definition: Form.java:298
com.google.appinventor.components.runtime.util.ViewUtil.setChildWidthForVerticalLayout
static void setChildWidthForVerticalLayout(View view, int width)
Definition: ViewUtil.java:89
com.google.appinventor.components.common.ComponentConstants.DEFAULT_PRIMARY_COLOR
static final String DEFAULT_PRIMARY_COLOR
Definition: ComponentConstants.java:88
com.google.appinventor.components.runtime.Form.runtimeFormErrorOccurredEvent
void runtimeFormErrorOccurredEvent(String functionName, int errorNumber, String message)
Definition: Form.java:1048
com.google.appinventor.components.runtime.Form.maybeShowTitleBar
void maybeShowTitleBar()
Definition: Form.java:2576
com.google.appinventor.components.runtime.ScaledFrameLayout.setScale
void setScale(float scale)
Definition: ScaledFrameLayout.java:92
com.google.appinventor.components.runtime.Form.onResume
void onResume()
Definition: Form.java:723
com.google.appinventor.components.runtime.Form.finishActivity
static void finishActivity()
Definition: Form.java:2224
com.google.appinventor.components.runtime.EventDispatcher.removeDispatchDelegate
static void removeDispatchDelegate(HandlesEventDispatching dispatchDelegate)
Definition: EventDispatcher.java:174
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.OnResumeListener
Definition: OnResumeListener.java:14
com.google.appinventor.components.runtime.Form.BackgroundColor
void BackgroundColor(int argb)
Definition: Form.java:1246
com.google.appinventor.components.runtime.Form.onCreateOptionsMenu
boolean onCreateOptionsMenu(Menu menu)
Definition: Form.java:2310
com.google.appinventor.components.runtime.Form.getOpenAnimType
String getOpenAnimType()
Definition: Form.java:1687
com.google.appinventor.components.runtime.util.AlignmentUtil
Definition: AlignmentUtil.java:18
com.google.appinventor.components.runtime.Form.isDarkTheme
boolean isDarkTheme()
Definition: Form.java:2584
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_TEXTAREA
static final String PROPERTY_TYPE_TEXTAREA
Definition: PropertyTypeConstants.java:248
com.google.appinventor.components.runtime.Form.finishApplication
static void finishApplication()
Definition: Form.java:2270
com.google.appinventor.components.runtime.Form.registerForActivityResult
void registerForActivityResult(ActivityResultListener listener, int requestCode)
Definition: Form.java:651
com.google.appinventor.components.runtime.Notifier
Definition: Notifier.java:78
com.google.appinventor.components.runtime.Form.$context
Activity $context()
Definition: Form.java:2105
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_THEME
static final String PROPERTY_TYPE_THEME
Definition: PropertyTypeConstants.java:323
com.google.appinventor.components.runtime.Form.Scrollable
void Scrollable(boolean scrollable)
Definition: Form.java:1145
com.google.appinventor.components.runtime.util.BulkPermissionRequest.onGranted
abstract void onGranted()
com.google.appinventor.components.runtime.PermissionResultHandler
Definition: PermissionResultHandler.java:15
com.google.appinventor.components.runtime.Form.CloseScreenAnimation
void CloseScreenAnimation(String animType)
Definition: Form.java:1672
com.google.appinventor.components.runtime.Form.setChildHeight
void setChildHeight(final AndroidViewComponent component, int height)
Definition: Form.java:2153
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_HORIZONTAL_ALIGNMENT
static final String PROPERTY_TYPE_HORIZONTAL_ALIGNMENT
Definition: PropertyTypeConstants.java:42
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_BAD_VALUE_FOR_HORIZONTAL_ALIGNMENT
static final int ERROR_BAD_VALUE_FOR_HORIZONTAL_ALIGNMENT
Definition: ErrorMessages.java:154
com.google.appinventor.components.runtime.VideoPlayer
Definition: VideoPlayer.java:121
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.util.AnimationUtil.ApplyOpenScreenAnimation
static void ApplyOpenScreenAnimation(Activity activity, String animType)
Definition: AnimationUtil.java:81
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_HONEYCOMB
static final int LEVEL_HONEYCOMB
Definition: SdkLevel.java:28
com.google.appinventor.components.runtime.OnClearListener
Definition: OnClearListener.java:16
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_SUBSET_JSON
static final String PROPERTY_TYPE_SUBSET_JSON
Definition: PropertyTypeConstants.java:254
com.google.appinventor.components.runtime.util.OnInitializeListener
Definition: OnInitializeListener.java:13
com.google.appinventor.components.runtime.util.FullScreenVideoUtil.performAction
synchronized Bundle performAction(int action, VideoPlayer source, Object data)
Definition: FullScreenVideoUtil.java:164
com.google.appinventor.components.runtime.util.SdkLevel
Definition: SdkLevel.java:19
com.google.appinventor.components.common.ComponentConstants.DEFAULT_ACCENT_COLOR
static final String DEFAULT_ACCENT_COLOR
Definition: ComponentConstants.java:90
com.google.appinventor.components.runtime.Form.AlignVertical
void AlignVertical(int alignment)
Definition: Form.java:1604
com.google.appinventor.components.runtime.util.FullScreenVideoUtil.FULLSCREEN_VIDEO_DIALOG_FLAG
static final int FULLSCREEN_VIDEO_DIALOG_FLAG
Definition: FullScreenVideoUtil.java:43
com.google.appinventor.components.runtime.Form.compatScalingFactor
float compatScalingFactor()
Definition: Form.java:2123
com.google.appinventor.components.runtime.collect.Maps
Definition: Maps.java:19
com.google.appinventor.components.runtime.Form.finishActivityWithTextResult
static void finishActivityWithTextResult(String result)
Definition: Form.java:2250
com.google.appinventor.components.runtime.ScaledFrameLayout
Definition: ScaledFrameLayout.java:30
com.google.appinventor.components.runtime.OnPauseListener
Definition: OnPauseListener.java:14
com.google.appinventor.components.runtime.Form.openAsset
InputStream openAsset(String asset)
Definition: Form.java:2752
com.google.appinventor.components.runtime.OnOrientationChangeListener
Definition: OnOrientationChangeListener.java:11
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.Form.getAssetPathForExtension
String getAssetPathForExtension(Component component, String asset)
Definition: Form.java:2765
com.google.appinventor.components.runtime.util.FullScreenVideoUtil.prepareFullScreenVideoDialog
void prepareFullScreenVideoDialog(Dialog dia)
Definition: FullScreenVideoUtil.java:341
com.google.appinventor.components.runtime.util.BulkPermissionRequest
Definition: BulkPermissionRequest.java:22
com.google.appinventor.components.runtime.collect.Maps.newHashMap
static< K, V > HashMap< K, V > newHashMap()
Definition: Maps.java:25
com.google.appinventor.components.runtime.Form.AboutScreen
void AboutScreen(String aboutScreen)
Definition: Form.java:1350
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.Form.registerForOnOptionsItemSelected
void registerForOnOptionsItemSelected(OnOptionsItemSelectedListener component)
Definition: Form.java:825
com.google.appinventor.components.runtime.Form.Title
void Title(String title)
Definition: Form.java:1317
com.google.appinventor.components.runtime.Form.AlignHorizontal
void AlignHorizontal(int alignment)
Definition: Form.java:1565
com.google.appinventor.components.runtime.LinearLayout.getLayoutManager
ViewGroup getLayoutManager()
Definition: LinearLayout.java:104
com.google.appinventor.components.runtime.Component.COLOR_BLACK
static final int COLOR_BLACK
Definition: Component.java:55
com.google.appinventor.components.runtime.errors.PermissionException
Definition: PermissionException.java:16
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.AlignmentUtil.setVerticalAlignment
void setVerticalAlignment(int alignment)
Definition: AlignmentUtil.java:51
com.google.appinventor.components.runtime.util.SdkLevel.getLevel
static int getLevel()
Definition: SdkLevel.java:45
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.util.ScreenDensityUtil.computeCompatibleScaling
static float computeCompatibleScaling(Context context)
Definition: ScreenDensityUtil.java:43
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.Map
Definition: Map.java:84
com.google.appinventor.components.runtime.collect.Lists
Definition: Lists.java:20
com.google.appinventor.components.runtime.Form.BackgroundColor
int BackgroundColor()
Definition: Form.java:1232
com.google.appinventor.components.runtime.OnCreateOptionsMenuListener
Definition: OnCreateOptionsMenuListener.java:16
com.google.appinventor.components.runtime.Deleteable
Definition: Deleteable.java:15
com.google.appinventor.components.runtime.AppInventorCompatActivity.Theme.DEVICE_DEFAULT
DEVICE_DEFAULT
Definition: AppInventorCompatActivity.java:45
com.google.appinventor.components.runtime.errors.PermissionException.getPermissionNeeded
String getPermissionNeeded()
Definition: PermissionException.java:32
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.runtime.ReplApplication
Definition: ReplApplication.java:32
com.google.appinventor.components.runtime.Form.$form
Form $form()
Definition: Form.java:2110
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.OnStopListener
Definition: OnStopListener.java:15
com.google.appinventor.components.runtime.AppInventorCompatActivity.Theme
Definition: AppInventorCompatActivity.java:42
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_SCREEN_ORIENTATION
static final String PROPERTY_TYPE_SCREEN_ORIENTATION
Definition: PropertyTypeConstants.java:212
com.google.appinventor.components.runtime.Form.finishActivityWithResult
static void finishActivityWithResult(Object result)
Definition: Form.java:2233
com.google.appinventor.components.runtime.Form.onRequestPermissionsResult
void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
Definition: Form.java:2701
com.google.appinventor.components.runtime.Form.onCreateDialog
Dialog onCreateDialog(int id)
Definition: Form.java:829
com.google.appinventor.components.runtime.Form.unregisterForActivityResult
void unregisterForActivityResult(ActivityResultListener listener)
Definition: Form.java:660
com.google.appinventor.components.runtime.Form.dispatchErrorOccurredEvent
void dispatchErrorOccurredEvent(final Component component, final String functionName, final int errorNumber, final Object... messageArgs)
Definition: Form.java:1011
com.google.appinventor.components.runtime.ActivityResultListener
Definition: ActivityResultListener.java:16
com.google.appinventor.components.runtime.util.JsonUtil.getObjectFromJson
static Object getObjectFromJson(String jsonString)
Definition: JsonUtil.java:317
com.google.appinventor.components.runtime.Form.registerPercentLength
void registerPercentLength(AndroidViewComponent component, int length, PercentStorageRecord.Dim dim)
Definition: Form.java:707
com.google.appinventor.components.runtime.Form.registerForOnPause
void registerForOnPause(OnPauseListener component)
Definition: Form.java:780
com.google.appinventor.components.common.ComponentConstants.VERTICAL_ALIGNMENT_DEFAULT
static final int VERTICAL_ALIGNMENT_DEFAULT
Definition: ComponentConstants.java:64
com.google.appinventor.components.runtime.Form.registerForOnStop
void registerForOnStop(OnStopListener component)
Definition: Form.java:793
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.runtime.AndroidViewComponent.getView
abstract View getView()
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_COLOR
static final String PROPERTY_TYPE_COLOR
Definition: PropertyTypeConstants.java:63
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_PERMISSION_DENIED
static final int ERROR_PERMISSION_DENIED
Definition: ErrorMessages.java:120
com.google.appinventor.components.runtime.Form.onActivityResult
void onActivityResult(int requestCode, int resultCode, Intent data)
Definition: Form.java:588
com.google.appinventor.components.runtime.Form.canDispatchEvent
boolean canDispatchEvent(Component component, String eventName)
Definition: Form.java:863
com.google.appinventor.components.runtime.AppInventorCompatActivity
Definition: AppInventorCompatActivity.java:40
com.google.appinventor.components.runtime.Form.setChildWidth
void setChildWidth(final AndroidViewComponent component, int width)
Definition: Form.java:2128
com.google.appinventor.components.runtime.Component.COLOR_WHITE
static final int COLOR_WHITE
Definition: Component.java:66
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_SCREEN_NOT_FOUND
static final int ERROR_SCREEN_NOT_FOUND
Definition: ErrorMessages.java:114
com.google.appinventor.components.runtime.util.ViewUtil.setChildHeightForVerticalLayout
static void setChildHeightForVerticalLayout(View view, int height)
Definition: ViewUtil.java:113
com.google
com
com.google.appinventor.components.runtime.Form.assertPermission
void assertPermission(String permission)
Definition: Form.java:2607
com.google.appinventor.components.runtime.OnOptionsItemSelectedListener
Definition: OnOptionsItemSelectedListener.java:16
com.google.appinventor.components.runtime.util.PaintUtil.hexStringToInt
static int hexStringToInt(String argb)
Definition: PaintUtil.java:45
com.google.appinventor.components.runtime.Form.addAboutInfoToMenu
void addAboutInfoToMenu(Menu menu)
Definition: Form.java:2336
com.google.appinventor.components.common.ComponentConstants.HORIZONTAL_ALIGNMENT_DEFAULT
static final int HORIZONTAL_ALIGNMENT_DEFAULT
Definition: ComponentConstants.java:63
com.google.appinventor.components.runtime.Form.unregisterPercentLength
void unregisterPercentLength(AndroidViewComponent component, PercentStorageRecord.Dim dim)
Definition: Form.java:713
com.google.appinventor.components.runtime.Form.clear
void clear()
Definition: Form.java:2397
com.google.appinventor.components.runtime.Form.deleteComponent
void deleteComponent(Object component)
Definition: Form.java:2427
com.google.appinventor.components.runtime.util.BulkPermissionRequest.getPermissions
final List< String > getPermissions()
Definition: BulkPermissionRequest.java:65
com.google.appinventor.components.runtime.Form.toastAllowed
boolean toastAllowed()
Definition: Form.java:2473
com.google.appinventor.components.runtime.MapFeatureContainerBase.iterator
Iterator< MapFeature > iterator()
Definition: MapFeatureContainerBase.java:347
com.google.appinventor.components.runtime.Form.getCompatibilityMode
static boolean getCompatibilityMode()
Definition: Form.java:2554
com.google.appinventor.components.runtime.errors
Definition: ArrayIndexOutOfBoundsError.java:7
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_VERTICAL_ALIGNMENT
static final String PROPERTY_TYPE_VERTICAL_ALIGNMENT
Definition: PropertyTypeConstants.java:43
com.google.appinventor.components.runtime.Form.AccentColor
int AccentColor()
Definition: Form.java:1884
com.google.appinventor.components.runtime.Form.deviceDensity
float deviceDensity()
Definition: Form.java:2119
com.google.appinventor.components.runtime.Form.dispatchPermissionDeniedEvent
void dispatchPermissionDeniedEvent(final Component component, final String functionName, final String permissionName)
Definition: Form.java:1001
com.google.appinventor.components.runtime.Form.updateTitle
void updateTitle()
Definition: Form.java:2571
com.google.appinventor.components.runtime.Form.onPrepareDialog
void onPrepareDialog(int id, Dialog dialog)
Definition: Form.java:838
com.google.appinventor.components.common.ComponentConstants
Definition: ComponentConstants.java:13
com.google.appinventor.components.runtime.multidex.MultiDex.install
static boolean install(Context context, boolean doIt)
Definition: MultiDex.java:94
com.google.appinventor.components.runtime.Notifier.ShowMessageDialog
void ShowMessageDialog(String message, String title, String buttonText)
Definition: Notifier.java:158
com.google.appinventor.components.runtime.Form.onGlobalLayout
void onGlobalLayout()
Definition: Form.java:536
com.google.appinventor.components.runtime.util.ScreenDensityUtil
Definition: ScreenDensityUtil.java:23
com.google.appinventor.components.runtime.LinearLayout
Definition: LinearLayout.java:20
com.google.appinventor.components.runtime.AndroidViewComponent
Definition: AndroidViewComponent.java:27
com.google.appinventor.components.runtime.Form.onStop
void onStop()
Definition: Form.java:785
com.google.appinventor.components.runtime.util.JsonUtil.getJsonRepresentation
static String getJsonRepresentation(Object value)
Definition: JsonUtil.java:246
com.google.appinventor.components.runtime.Form.registerForOnClear
void registerForOnClear(OnClearListener component)
Definition: Form.java:797
com.google.appinventor.components.common.ComponentConstants.LAYOUT_ORIENTATION_VERTICAL
static final int LAYOUT_ORIENTATION_VERTICAL
Definition: ComponentConstants.java:27
com.google.appinventor.components.runtime.Form.registerForOnCreateOptionsMenu
void registerForOnCreateOptionsMenu(OnCreateOptionsMenuListener component)
Definition: Form.java:821
com.google.appinventor.components.runtime.Form.getActiveForm
static Form getActiveForm()
Definition: Form.java:2181
com.google.appinventor.components.runtime.Form.registerForActivityResult
int registerForActivityResult(ActivityResultListener listener)
Definition: Form.java:638
com.google.appinventor.components.annotations.IsColor
Definition: IsColor.java:13
com.google.appinventor.components.runtime.Form
Definition: Form.java:126
com.google.appinventor.components.runtime.Form.dispatchErrorOccurredEventDialog
void dispatchErrorOccurredEventDialog(final Component component, final String functionName, final int errorNumber, final Object... messageArgs)
Definition: Form.java:1026
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_ASSET
static final String PROPERTY_TYPE_ASSET
Definition: PropertyTypeConstants.java:22
com.google.appinventor.components.runtime.Form.dontGrabTouchEventsForComponent
void dontGrabTouchEventsForComponent()
Definition: Form.java:2460
com.google.appinventor.components.runtime.Form.onConfigurationChanged
void onConfigurationChanged(Configuration newConfig)
Definition: Form.java:468
com.google.appinventor.components.runtime.AppInventorCompatActivity.Theme.BLACK_TITLE_TEXT
BLACK_TITLE_TEXT
Definition: AppInventorCompatActivity.java:46
com.google.appinventor.components.annotations.PropertyCategory.APPEARANCE
APPEARANCE
Definition: PropertyCategory.java:16
com.google.appinventor.components.runtime.Form.onBackPressed
void onBackPressed()
Definition: Form.java:570
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.runtime.OnDestroyListener
Definition: OnDestroyListener.java:15
com.google.appinventor.components.runtime.Form.addExitButtonToMenu
void addExitButtonToMenu(Menu menu)
Definition: Form.java:2324
com.google.appinventor.components.annotations
com.google.appinventor.components.runtime.Form.isDeniedPermission
boolean isDeniedPermission(String permission)
Definition: Form.java:2596
com.google.appinventor.components.runtime.AppInventorCompatActivity.Theme.CLASSIC
CLASSIC
Definition: AppInventorCompatActivity.java:44
com.google.appinventor
com.google.appinventor.components.runtime.Form.ErrorOccurredDialog
void ErrorOccurredDialog(Component component, String functionName, int errorNumber, String message, String title, String buttonText)
Definition: Form.java:960
com.google.appinventor.components.runtime.util.ErrorMessages.ERROR_INVALID_SCREEN_ORIENTATION
static final int ERROR_INVALID_SCREEN_ORIENTATION
Definition: ErrorMessages.java:113
com.google.appinventor.components.runtime.util.AnimationUtil.ApplyCloseScreenAnimation
static void ApplyCloseScreenAnimation(Activity activity, String animType)
Definition: AnimationUtil.java:121
com.google.appinventor.components.runtime.Form.registerForOnNewIntent
void registerForOnNewIntent(OnNewIntentListener component)
Definition: Form.java:767
com.google.appinventor.components.runtime.Form.switchFormWithStartValue
static void switchFormWithStartValue(String nextFormName, Object startValue)
Definition: Form.java:2027
com.google.appinventor.components.runtime.collect.Sets
Definition: Sets.java:23
com.google.appinventor.components.runtime.util.ViewUtil
Definition: ViewUtil.java:22