diff --git a/README.md b/README.md
index 03cd302..c4a3fc4 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
# DynamicComponents-AI2 `Extension`
+[![forthebadge](https://forthebadge.com/images/badges/its-not-a-lie-if-you-believe-it.svg)](https://forthebadge.com)
+
[![Maintainability](https://api.codeclimate.com/v1/badges/31e4cd31de1bd0e186c8/maintainability)](https://codeclimate.com/github/ysfchn/DynamicComponents-AI2/maintainability)
Fully supported Dynamic Components extension for MIT App Inventor 2. It is based on Java's reflection feature, so it creates the components by searching for a class by just typing its name. So it doesn't have a limited support for specific components, because it supports every component which is ever added to your App Inventor distribution!
@@ -10,7 +12,13 @@ So if you use Kodular, you will able to create all Kodular components, if you us
> ⚠ The `beta` branch will be reset after every release. So stay on the `main` branch if you don't know what you do.
-[![forthebadge](https://forthebadge.com/images/badges/its-not-a-lie-if-you-believe-it.svg)](https://forthebadge.com)
+---
+
+### Asynchronous support
+
+This extension can create components asynchronously or synchronously based on your choice. If you don't want to block the main app during creating a bunch of components, go to the Designer (after importing the extension) and select between "UI" (asynchronous) and "Main" (synchronous).
+
+
## 🧩 Blocks
@@ -78,7 +86,10 @@ So if you use Kodular, you will able to create all Kodular components, if you us
-->
- Changes ID of one of created components to a new one. The old ID must be exist and new ID mustn't exist.
+ Changes ID of one of created components to a new one. The old ID must be exist and new ID mustn't exist.
+
+ When replacing an ID, it will loop through ALL IDs and replace each one that contains the old ID! Even if the ID you're wanting to replace is "Timmy", but there are IDs like "Timmy-truck". To be clear again, only "Timmy" from "Timmy-truck" will be replaced, the "-truck" part will stay the same!
+
@@ -214,23 +225,6 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Component --> ID
-
-
-
-
-
-
-
- Returns the internal/class name of any component or object. The returned value can be also used in Create block.
-
-
@@ -318,21 +312,40 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Get a property value of a component by typing its property name. Can be known as a Getter property block. It can be also used to get properties that only exists in Designer.
-
+
-
+
-
- Gives the information of the specified component with all properties, events, methods as JSON text.
+ Get meta data about the specified component.
+
+
+
+
+
+
+
+
+ Get meta data about events for the specified component.
+
+
+
+
+
+
+
+
+ Get meta data about properties for the specified component.
+
+
+
+
+
+
+
+
+ Get meta data about functions for the specified component.
@@ -353,10 +366,10 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Returns all used IDs of current components as App Inventor list.
-
+
-
+
Makes a random unique UUID. Use this block in Create block if component ID is not required for you.
@@ -398,16 +411,6 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Returns the version name of the extension.
-
-
-
-
-
-
-
- Sets whether component creation should work asynchronously or synchronously.
-
-
@@ -417,13 +420,13 @@ So if you use Kodular, you will able to create all Kodular components, if you us
Raises after Schema has been created with Schema block.
-
+
-
+
- Raises after a component has been created using the Create block. It also will be raised for components that created with Schema.
+ Raises after a component has been created.
diff --git a/assets/blocks/event_componentbuilt.png b/assets/blocks/event_componentbuilt.png
new file mode 100644
index 0000000..77bffe6
Binary files /dev/null and b/assets/blocks/event_componentbuilt.png differ
diff --git a/assets/blocks/event_componentcreated.png b/assets/blocks/event_componentcreated.png
deleted file mode 100644
index 1b4baac..0000000
Binary files a/assets/blocks/event_componentcreated.png and /dev/null differ
diff --git a/assets/blocks/method_generateid.png b/assets/blocks/method_generateid.png
new file mode 100644
index 0000000..afc3ee1
Binary files /dev/null and b/assets/blocks/method_generateid.png differ
diff --git a/assets/blocks/method_getcomponentmeta.png b/assets/blocks/method_getcomponentmeta.png
new file mode 100644
index 0000000..ec1cf4e
Binary files /dev/null and b/assets/blocks/method_getcomponentmeta.png differ
diff --git a/assets/blocks/method_geteventmeta.png b/assets/blocks/method_geteventmeta.png
new file mode 100644
index 0000000..3ad366e
Binary files /dev/null and b/assets/blocks/method_geteventmeta.png differ
diff --git a/assets/blocks/method_getfunctionmeta.png b/assets/blocks/method_getfunctionmeta.png
new file mode 100644
index 0000000..87f373a
Binary files /dev/null and b/assets/blocks/method_getfunctionmeta.png differ
diff --git a/assets/blocks/method_getname.png b/assets/blocks/method_getname.png
deleted file mode 100644
index 14ff7d7..0000000
Binary files a/assets/blocks/method_getname.png and /dev/null differ
diff --git a/assets/blocks/method_getpropertymeta.png b/assets/blocks/method_getpropertymeta.png
new file mode 100644
index 0000000..e256e91
Binary files /dev/null and b/assets/blocks/method_getpropertymeta.png differ
diff --git a/assets/blocks/method_listdetails.png b/assets/blocks/method_listdetails.png
deleted file mode 100644
index 05de1a3..0000000
Binary files a/assets/blocks/method_listdetails.png and /dev/null differ
diff --git a/assets/blocks/method_randomuuid.png b/assets/blocks/method_randomuuid.png
deleted file mode 100644
index 94df1bb..0000000
Binary files a/assets/blocks/method_randomuuid.png and /dev/null differ
diff --git a/assets/blocks/setget_async.png b/assets/blocks/setget_async.png
deleted file mode 100644
index f942f09..0000000
Binary files a/assets/blocks/setget_async.png and /dev/null differ
diff --git a/assets/blocks/setget_async_2.png b/assets/blocks/setget_async_2.png
deleted file mode 100644
index 9542ff7..0000000
Binary files a/assets/blocks/setget_async_2.png and /dev/null differ
diff --git a/assets/icon.png b/assets/icon.png
index 36c685f..1c688f6 100644
Binary files a/assets/icon.png and b/assets/icon.png differ
diff --git a/assets/other/method_getdesignerproperties.png b/assets/other/method_getdesignerproperties.png
deleted file mode 100644
index 034884a..0000000
Binary files a/assets/other/method_getdesignerproperties.png and /dev/null differ
diff --git a/assets/other/thread_choice.png b/assets/other/thread_choice.png
new file mode 100644
index 0000000..d1a026a
Binary files /dev/null and b/assets/other/thread_choice.png differ
diff --git a/src/com/yusufcihan/DynamicComponents/DynamicComponents.java b/src/com/yusufcihan/DynamicComponents/DynamicComponents.java
index 09743bc..db387a9 100644
--- a/src/com/yusufcihan/DynamicComponents/DynamicComponents.java
+++ b/src/com/yusufcihan/DynamicComponents/DynamicComponents.java
@@ -14,17 +14,21 @@
import com.google.appinventor.components.runtime.Component;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
+import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.errors.YailRuntimeError;
-import com.google.appinventor.components.runtime.util.YailList;
import com.google.appinventor.components.runtime.util.YailDictionary;
+import com.google.appinventor.components.runtime.util.YailList;
import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
-import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
+import gnu.lists.FString;
+import gnu.math.DFloNum;
+import gnu.math.IntNum;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
@@ -32,803 +36,556 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Objects;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@DesignerComponent(
- description = "Dynamic Components is an extension that creates any component in your App Inventor distribution programmatically, instead of having pre-defined components. Made with ❤️ by Yusuf Cihan",
+ description = "Dynamic Components is an extension that creates any component in your App Inventor distribution programmatically, instead of having pre-defined components. Made with ❤️ by Yusuf Cihan.",
category = ComponentCategory.EXTENSION,
- helpUrl = "https://github.com/ysfchn/DynamicComponents-AI2",
+ helpUrl = "https://github.com/ysfchn/DynamicComponents-AI2/blob/main/README.md",
iconName = "aiwebres/icon.png",
nonVisible = true,
- version = 6,
- versionName = "2.1.0"
+ version = 7,
+ versionName = "2.2.0"
)
@SimpleObject(external = true)
public class DynamicComponents extends AndroidNonvisibleComponent {
- private Activity activity;
- private Internal internal = null;
- private boolean isAsync = false;
-
- /**
- * Contains the created components. Key is the ID of the components, and their values are the components
- * that created with Create block.
- */
- private final HashMap COMPONENTS = new HashMap<>();
-
- /**
- * Specifies the base package for creating the components.
- */
- private final String BASE_PACKAGE = "com.google.appinventor.components.runtime";
-
- /**
- * Stores the component template. Needs to be cleared before rendering Schema operation.
- */
- private JSONArray PROPERTIESARRAY = new JSONArray();
+ // Base package name for components
+ private final String BASE = "com.google.appinventor.components.runtime.";
+
+ // Whether component creation should happen on the UI thread
+ private boolean postOnUiThread = false;
+
+ // Components created with Dynamic Components
+ private final HashMap COMPONENTS = new HashMap();
+
+ // IDs of components created with Dynamic Components
+ private final HashMap COMPONENT_IDS = new HashMap();
+
+ private Object lastUsedId = "";
+ private ArrayList componentListeners = new ArrayList();
+ private JSONArray propertiesArray = new JSONArray();
+ private final Util UTIL_INSTANCE = new Util();
public DynamicComponents(ComponentContainer container) {
super(container.$form());
- this.activity = container.$context();
- this.internal = new Internal();
}
- /**
- * Raises after a schema has created with the "Schema" block.
- *
- * @param name Name of the template.
- * @param parameters The JSON parameters in the schema.
- */
- @SimpleEvent(description = "Raises after a schema has created with the \"Schema\" block.")
- public void SchemaCreated(String name, YailList parameters) {
- EventDispatcher.dispatchEvent(this, "SchemaCreated", name, parameters);
- }
-
- /**
- * Raises after a component has been created using the Create block.
- *
- * @param id The ID of the created component.
- * @param type Type type name of the created component.
- */
- @SimpleEvent(description = "Raises after a component has been created using the Create block. It also will be raised for components that created with Schema.")
- public void ComponentCreated(String id, String type) {
- EventDispatcher.dispatchEvent(this, "ComponentCreated", id, type);
+ interface ComponentListener {
+ public void onCreation(Component component, String id);
}
- @DesignerProperty(defaultValue = "False", editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN)
- @SimpleProperty(description = "Sets whether component creation should rely on the UI thread.")
- public void Async(boolean async) {
- isAsync = async;
- }
-
- @SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "Gets whether component creation should rely on the UI thread.")
- public boolean Async() {
- return isAsync;
- }
-
- /**
- * Creates a new dynamic component. It supports all component that added to your
- * current AI2 distribution. In componentName, you can type the component's name
- * like "Button", or you can pass a static component then it can create a new
- * instance of it.
- *
- * @param in Layout/Component where the created component will be placed.
- * @param componentName Name/FQCN of the component that will be created.
- * @param id A unique identifier for this component.
- */
- @SimpleFunction(description = "Creates a new dynamic component. It supports all component that added to your current AI2 distribution. In componentName, you can type the component's name like 'Button', or you can pass a static component then it can create a new instance of it, or just type the full class name of component.")
- public void Create(final AndroidViewComponent in, final Object componentName, final String id) {
- if (isAsync) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- CreateMethod(in, componentName, id);
- }
- });
- } else {
- CreateMethod(in, componentName, id);
+ class Util {
+ public boolean exists(Component component) {
+ return COMPONENTS.containsValue(component);
}
- }
- public void CreateMethod(final AndroidViewComponent in, final Object componentName, final String id) {
- String className = internal.checkId(componentName, id);
+ public boolean exists(String id) {
+ return COMPONENTS.containsKey(id);
+ }
- try {
- if (!"".equals(className)) {
- // Create a Class object from class name.
- Class> classObject = Class.forName(className.trim().replace(" ", ""));
+ public Method getMethod(Method[] methods, String name, int parameterCount) {
+ name = name.replaceAll("[^a-zA-Z0-9]", "");
+ for (Method method : methods) {
+ int methodParameterCount = method.getParameterTypes().length;
+ if (method.getName().equals(name) && methodParameterCount == parameterCount) {
+ return method;
+ }
+ }
- // Create constructor object for creating a new instance.
- Constructor> constructor = classObject.getConstructor(ComponentContainer.class);
+ return null;
+ }
- // Create a new instance of specified component.
- Component component = (Component) constructor.newInstance((ComponentContainer) in);
+ public void newInstance(Constructor> constructor, String id, AndroidViewComponent input) {
+ Component mComponent = null;
- if (className.contains("ImageSprite") || className.contains("Sprite")) {
- Invoke(component, "Initialize", new YailList());
+ try {
+ mComponent = (Component) constructor.newInstance((ComponentContainer) input);
+ } catch(Exception e) {
+ throw new YailRuntimeError(e.getMessage(), "DynamicComponents");
+ } finally {
+ if (mComponent != null) {
+ String mComponentClassName = mComponent.getClass().getSimpleName();
+ if (mComponentClassName == "ImageSprite" || mComponentClassName == "Sprite") {
+ Invoke(mComponent, "Initialize", new YailList());
+ }
+
+ COMPONENT_IDS.put(mComponent, id);
+ COMPONENTS.put(id, mComponent);
+ this.notifyListenersOfCreation(mComponent, id);
+ ComponentBuilt(mComponent, id, mComponentClassName);
}
-
- // Save the component.
- COMPONENTS.put(id, component);
-
- // Finalize component creation.
- ComponentCreated(id, classObject.getSimpleName());
}
- } catch (Exception exception) {
- throw new YailRuntimeError("DynamicComponents-AI2: " + exception.toString(), "Error");
}
- }
-
- public void SchemaMethod(final AndroidViewComponent in, final YailList parameters, final String template) {
- try {
- // Remove the contents of the array by creating a new JSONArray.
- PROPERTIESARRAY = new JSONArray();
- // Create a JSONObject from template for checking.
- JSONObject j = new JSONObject(template);
- // Save the template string to a new variable for editing.
- String modifiedTemplate = internal.replaceKeys(j, parameters, template);
- // Lastly parse the JSONObject.
- internal.parseJson("", new JSONObject(modifiedTemplate));
- // Delete the first element, because it contains metadata instead of components.
- PROPERTIESARRAY.remove(0);
-
- // Start creating the extensions (finally).
- for (int i = 0; i < PROPERTIESARRAY.length(); i++) {
- // Check if component has an ID key.
- if (!PROPERTIESARRAY.getJSONObject(i).has("id")) {
- throw new YailRuntimeError("One or more of the components has not an ID in template!", "Error");
- }
- internal.createBySchema(in, i, PROPERTIESARRAY);
+ public void parse(String id, JSONObject json) {
+ JSONObject data = new JSONObject(json.toString());
+ data.remove("components");
- // If JSONObject contains a "properties" section, then set its properties with
- // Invoke block.
- if (PROPERTIESARRAY.getJSONObject(i).has("properties")) {
- internal.setPropertiesBySchema(i, PROPERTIESARRAY);
- }
+ if (!"".equals(id)) {
+ data.put("in", id);
}
- SchemaCreated(j.optString("name"), parameters);
- } catch (Exception e) {
- throw new YailRuntimeError(e.getMessage(), "Error");
- }
- }
+ propertiesArray.put(data);
- /**
- * Imports a JSON string that is a template for creating the dynamic components
- * automatically with single block. Templates can also contain parameters that
- * will be replaced with the values which defined in the "parameters" list.
- *
- * @param in Layout/Component where the created component will be placed.
- * @param template A JSON string containing information for creating the component.
- * @param parameters Data for parameters defined in the above JSON string.
- */
- @SimpleFunction(description = "Imports a JSON string that is a template for creating the dynamic components automatically with single block. Templates can also contain parameters that will be replaced with the values which defined in the 'parameters' list.")
- public void Schema(final AndroidViewComponent in, final String template, final YailList parameters) {
- if (isAsync) {
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- SchemaMethod(in, parameters, template);
+ if (json.has("components")) {
+ for (int i = 0; i < json.getJSONArray("components").length(); i++) {
+ this.parse(data.optString("id", ""), json.getJSONArray("components").getJSONObject(i));
}
- });
- } else {
- SchemaMethod(in, parameters, template);
+ }
}
- }
- /**
- * Replaces the specified ID with the given new ID. The old ID must be bound to
- * a component and the new ID must be unique, i.e., not used already.
- *
- * @param id Existing ID of the specified component.
- * @param newId The ID to replace existing one with.
- */
- @SimpleFunction(description = "Replaces the specified ID with the given new ID. The old ID must be bound to a component and the new ID must be unique, i.e., not used already.")
- public void ChangeId(String id, String newId) {
- if (COMPONENTS.containsKey(id) && !COMPONENTS.containsKey(newId)) {
- Component component = COMPONENTS.remove(id);
- COMPONENTS.put(newId, component);
- } else {
- throw new YailRuntimeError("Old ID must exist and new ID mustn't exist.", "Error");
+ public void notifyListenersOfCreation(Component component, String id) {
+ for (ComponentListener listener : componentListeners) {
+ listener.onCreation(component, id);
+ }
}
}
- /**
- * Moves the specified component to the given arrangement.
- *
- * @param arrangement Target arrangement in which the specified component should be transferred.
- * @param component Component that needs to be transferred.
- */
- @SimpleFunction(description = "Moves the specified component to the given arrangement.")
- public void Move(AndroidViewComponent arrangement, AndroidViewComponent component) {
- View comp = component.getView();
- ViewGroup source = (ViewGroup)comp.getParent();
- source.removeView(comp);
-
- ViewGroup vg2 = (ViewGroup)arrangement.getView();
- ViewGroup target = (ViewGroup)vg2.getChildAt(0);
-
- target.addView(comp);
- }
-
- /**
- * Returns the position of the component with respect to other sibling components
- * in it's parent arrangement. Index starts from 1.
- *
- * @param component Target component.
- * @return Position index of the specified component in the parent arrangement.
- */
- @SimpleFunction(description = "Returns the position of the component with respect to other sibling components in it's parent arrangement. Index starts from 1.")
- public int GetOrder(AndroidViewComponent component){
- View comp = component.getView();
- View parent = (View) component.getView().getParent();
-
- if (comp != null && parent != null) {
- View v = component.getView();
- ViewGroup vg = (ViewGroup)v.getParent();
- int index = vg.indexOfChild(v);
- return index + 1;
- } else {
- return 0;
- }
+ @DesignerProperty(
+ defaultValue = "UI",
+ editorArgs = {"Main", "UI"},
+ editorType = PropertyTypeConstants.PROPERTY_TYPE_CHOICES
+ )
+ @SimpleProperty(userVisible = false)
+ public void Thread(String thread) {
+ postOnUiThread = (thread == "UI");
+ }
+
+ @Deprecated
+ @SimpleEvent(description = "Do NOT use this event. Use 'ComponentBuilt' as a replacement.")
+ public void ComponentCreated(String id, String type) {
+ EventDispatcher.dispatchEvent(this, "ComponentCreated", id, type);
}
- /**
- * Sets the position of the specified component with respect to other sibling
- * components in it's parent arrangement. Index starts from 1. Typing 0 (zero)
- * will move the component to the end.
- *
- * @param component Target component.
- * @param index Index at which the component should be placed in it's parent component.
- */
- @SimpleFunction(description = "Sets the position of the specified component with respect to other sibling components in it's parent arrangement. Index starts from 1. Typing 0 (zero) will move the component to the end.")
- public void SetOrder(AndroidViewComponent component, int index) {
- View comp = component.getView();
- ViewGroup source = (ViewGroup)comp.getParent();
- source.removeView(comp);
-
- int i = index - 1;
- int childCount = source.getChildCount();
-
- if (i > childCount) {
- i = childCount;
- }
-
- source.addView(comp, i);
+ @SimpleEvent(description = "Is called after a component has been created.")
+ public void ComponentBuilt(Component component, String id, String type) {
+ EventDispatcher.dispatchEvent(this, "ComponentBuilt", component, id, type);
}
- /**
- * Removes the component with specified ID from screen/layout and the component
- * list. So you will able to use its ID again as it will be deleted.
- *
- * @param id The ID of the component that is supposed to be removed.
- */
- @SimpleFunction(description = "Removes the component with specified ID from screen/layout and the component list so you can use its ID again after it's deleted.")
- public void Remove(String id) {
- if (internal.isIdTaken(id)) {
- Object component = COMPONENTS.get(id);
+ @SimpleEvent(description = "Is called after a schema has/mostly finished component creation.")
+ public void SchemaCreated(String name, YailList parameters) {
+ EventDispatcher.dispatchEvent(this, "SchemaCreated", name, parameters);
+ }
- try {
- if (component != null) {
- Method method = component.getClass().getMethod("getView");
- View componentView = (View) method.invoke(component);
- ViewGroup parent = (ViewGroup) componentView.getParent();
- parent.removeView(componentView);
+ @SimpleFunction(description = "Assign a new ID to a previously created dynamic component.")
+ public void ChangeId(String id, String newId) {
+ if (UTIL_INSTANCE.exists(id) && !UTIL_INSTANCE.exists(newId)) {
+ for (String mId : UsedIDs().toStringArray()) {
+ if (mId.contains(id)) {
+ Component mComponent = (Component) GetComponent(mId);
+ String mReplacementId = mId.replace(id, newId);
+ COMPONENT_IDS.remove(mComponent);
+ COMPONENTS.put(mReplacementId, COMPONENTS.remove(mId));
+ COMPONENT_IDS.put(mComponent, mReplacementId);
}
- } catch (Exception e) {
- e.printStackTrace();
}
-
- COMPONENTS.remove(id);
+ } else {
+ throw new YailRuntimeError("The ID you used is either not a dynamic component, or the ID you've used to replace the old ID is already taken.", "DynamicComponents");
}
}
- /**
- * Returns the ID of the last component created using the "Create" block.
- *
- * @return The last used ID.
- */
- @SimpleFunction(description = "Returns the ID of the last component created using the \"Create\" block.")
- public String LastUsedID() {
- Object[] COMPONENT_IDS = COMPONENTS.keySet().toArray();
- return (COMPONENT_IDS.length > 0 ? COMPONENT_IDS[COMPONENT_IDS.length - 1].toString() : "");
+ @SimpleFunction(description = "Creates a new dynamic component.")
+ public void Create(final AndroidViewComponent in, Object componentName, final String id) throws Exception {
+ componentName = componentName.toString().replaceAll("[^a-zA-Z0-9]", "");
+ if (!COMPONENTS.containsKey(id)) {
+ lastUsedId = id;
+
+ String mClassName = BASE + componentName;
+ Class> mClass = Class.forName(mClassName);
+ final Constructor> mConstructor = mClass.getConstructor(ComponentContainer.class);
+
+ if (postOnUiThread) {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ UTIL_INSTANCE.newInstance(mConstructor, id, in);
+ }
+ });
+ } else {
+ UTIL_INSTANCE.newInstance(mConstructor, id, in);
+ }
+
+ ComponentCreated(id.toString(), mClass.getSimpleName());
+ } else {
+ throw new YailRuntimeError("ID must be unique.", "DynamicComponents");
+ }
}
- /**
- * Returns a random UUID.
- *
- * @return A random UUID.
- */
- @SimpleFunction(description = "Generates a unique ID that can be used to create a component.")
- public String RandomUUID() {
- String uuid = "";
+ @SimpleFunction(description = "Generates a random ID to create a component with.")
+ public String GenerateID() {
+ String id = "";
do {
- uuid = UUID.randomUUID().toString();
- } while (internal.isIdTaken(uuid));
+ id = UUID.randomUUID().toString();
+ } while (UTIL_INSTANCE.exists(id));
- return uuid;
+ return id;
}
- /**
- * Returns a list of IDs of every created component.
- *
- * @return A list of IDs.
- */
- @SimpleFunction(description = "Returns a list of IDs of every created component.")
- public YailList UsedIDs() {
- Set keys = COMPONENTS.keySet();
- return YailList.makeList(keys);
- }
-
- /**
- * Returns the component itself for setting properties. ID must be a valid ID
- * which is added with Create block. ID --> Component
- *
- * @param id ID of the desired component.
- * @return A component bound to the given ID.
- */
- @SimpleFunction(description = "Returns the component itself for setting properties. ID must be a valid ID which is added with Create block.")
+ @SimpleFunction(description = "Returns the component associated with the specified ID.")
public Object GetComponent(String id) {
return COMPONENTS.get(id);
}
- /**
- * Returns "true" if the specified component is created by Dynamic Components
- * extension. Otherwise, "false".
- *
- * @param component Component that needs to be analysed.
- * @return A boolean value indicating whether the specified component is dynamically created or not.
- */
- @SimpleFunction(description = "Returns 'true' if component was created by the Dynamic Components extension. Otherwise, 'false'.")
- public Object IsDynamic(Component component) {
- return internal.isDynamicComponent(component);
- }
-
- /**
- * Returns the ID of the specified component. The desired component must created using the "Create" block, else it will return a blank string. Component --> ID
- *
- * @param component The component that its ID will be returned.
- * @return ID of the specified component.
- */
- @SimpleFunction(description = "Returns the ID of the specified component. The desired component must created using the \"Create\" block, else it will return a blank string. Component --> ID")
- public String GetId(Component component) {
- // Getting key from value,
- for (String o : COMPONENTS.keySet()) {
- if (COMPONENTS.get(o).equals(component)) {
- return o;
- }
+ @SimpleFunction(description = "Get meta data about the specified component.")
+ public YailDictionary GetComponentMeta(Component component) {
+ Class> mClass = component.getClass();
+ DesignerComponent mDesignerAnnotation = mClass.getAnnotation(DesignerComponent.class);
+ SimpleObject mObjectAnnotation = mClass.getAnnotation(SimpleObject.class);
+ YailDictionary mMeta = new YailDictionary();
+
+ if (mDesignerAnnotation != null) {
+ mMeta.put("androidMinSdk", mDesignerAnnotation.androidMinSdk());
+ mMeta.put("category", mDesignerAnnotation.category());
+ mMeta.put("dateBuilt", mDesignerAnnotation.dateBuilt());
+ mMeta.put("description", mDesignerAnnotation.description());
+ mMeta.put("designerHelpDescription", mDesignerAnnotation.designerHelpDescription());
+ mMeta.put("external", mObjectAnnotation.external());
+ mMeta.put("helpUrl", mDesignerAnnotation.helpUrl());
+ mMeta.put("iconName", mDesignerAnnotation.iconName());
+ mMeta.put("nonVisible", mDesignerAnnotation.nonVisible());
+ mMeta.put("package", mClass.getName());
+ mMeta.put("showOnPalette", mDesignerAnnotation.showOnPalette());
+ mMeta.put("type", mClass.getSimpleName());
+ mMeta.put("version", mDesignerAnnotation.version());
+ mMeta.put("versionName", mDesignerAnnotation.versionName());
}
- return "";
+ return mMeta;
}
- /**
- * Returns the internal name of any component or object.
- *
- * @param component The component that its internal name needs to be returned.
- * @return Internal name of the specified component.
- */
- @SimpleFunction(description = "Returns the internal name of any component or object.")
- public String GetName(Object component) {
- return internal.getClass(component).getName();
- }
-
- /**
- * Sets the specified property of the component to the given value. It can be also
- * used to set properties that only exists in Designer. Supported values are:
- * "string", "boolean", "integer" and "float". For other values, you should use
- * "Any Component" blocks.
- *
- * @param component The component that property will set for.
- * @param name The name of the property that needs to be set.
- * @param value The value that needs to be set of the specified property.
- */
- @SimpleFunction(description = "Sets the specified property of the component to the given value. It can be also used to set properties that only exists in Designer. Supported values are; 'string', 'boolean', 'integer' and 'float'. For other values, you should use Any Component blocks.")
- public void SetProperty(Component component, String name, Object value) {
- // The method will be invoked.
- try {
- Invoke(component, name, YailList.makeList(new Object[] { value }));
- } catch (Exception exception) {
- throw new YailRuntimeError(exception.getMessage(), "Error");
- }
- }
-
- /**
- * Set multiple properties of a component by typing its property name and value.
- * It behaves like a Setter property block. It can be also used to set properties
- * that only exists in Designer. Supported values are; "string", "boolean",
- * "integer" and "float". For other values, you should use Any Component blocks.
- *
- * @param component The component that property will set for.
- * @param properties Properties and their respective values as a dictionary.
- * @throws JSONException Creating new instance of JSONObject throws this exception.
- */
- @SimpleFunction(description = "Set multiple properties of a component at once.")
- public void SetProperties(Component component, YailDictionary properties) throws JSONException {
- JSONObject propertyObject = new JSONObject(properties.toString());
- JSONArray names = propertyObject.names();
-
- for (int i = 0; i < propertyObject.length(); i++) {
- String name = names.getString(i);
- Object value = propertyObject.get(name);
- Invoke(component, name, YailList.makeList(new Object[] { value }));
+ @SimpleFunction(description = "Get meta data about events for the specified component.")
+ public YailDictionary GetEventMeta(Component component) {
+ Method[] mMethods = component.getClass().getMethods();
+ YailDictionary mEvents = new YailDictionary();
+
+ for (Method mMethod : mMethods) {
+ SimpleEvent mAnnotation = mMethod.getAnnotation(SimpleEvent.class);
+ String mName = mMethod.getName();
+ YailDictionary mEventMeta = new YailDictionary();
+
+ if (mAnnotation != null) {
+ mEventMeta.put("description", mAnnotation.description());
+ mEventMeta.put("isDeprecated", (mMethod.getAnnotation(Deprecated.class) != null));
+ mEventMeta.put("userVisible", mAnnotation.userVisible());
+ mEvents.put(mName, mEventMeta);
+ }
}
- }
- /**
- * Returns the specified properties value of the given component. Can be known
- * as a Getter property block. It can be also used to get properties that only
- * exists in Designer.
- *
- * @param component The component that property will get from.
- * @param name Name of the desired property.
- * @return Value of the specified property of the specified component.
- */
- @SimpleFunction(description = "Returns the specified properties value of the given component. Can be known as a Getter property block. It can be also used to get properties that only exists in Designer.")
- public Object GetProperty(Component component, String name) {
- // The method will be invoked.
- try {
- return Invoke(component, name, YailList.makeEmptyList());
- } catch (Exception exception) {
- // Throw an error when something goes wrong.
- throw new YailRuntimeError("" + exception, "Error");
- }
+ return mEvents;
}
- /**
- * Invokes the specified method of the specified component.
- *
- * @param component The component that method will be executed for.
- * @param name Name of the method that needs to be invoked.
- * @param parameters Parameters to pass to the specified method.
- * @return Return value of the invoked method.
- */
- @SimpleFunction(description = "Invokes a method with parameters.")
- public Object Invoke(Component component, String name, YailList parameters) {
- // The method will be invoked.
- name = name.trim().replace(" ", "");
-
- try {
- if (component == null) {
- throw new YailRuntimeError("Component is not specified.", "Error");
+ @SimpleFunction(description = "Get meta data about functions for the specified component.")
+ public YailDictionary GetFunctionMeta(Component component) {
+ Method[] mMethods = component.getClass().getMethods();
+ YailDictionary mFunctions = new YailDictionary();
+
+ for (Method mMethod : mMethods) {
+ SimpleFunction mAnnotation = mMethod.getAnnotation(SimpleFunction.class);
+ String mName = mMethod.getName();
+ YailDictionary mFunctionMeta = new YailDictionary();
+
+ if (mAnnotation != null) {
+ mFunctionMeta.put("description", mAnnotation.description());
+ mFunctionMeta.put("isDeprecated", (mMethod.getAnnotation(Deprecated.class) != null));
+ mFunctionMeta.put("userVisible", mAnnotation.userVisible());
+ mFunctions.put(mName, mFunctionMeta);
}
+ }
- Method method = internal.findMethod(internal.getClass(component).getMethods(), name.replace(" ", ""), parameters.toArray().length);
+ return mFunctions;
+ }
- if (method == null) {
- throw new YailRuntimeError("Method can't found with that name.", "Error");
- }
+ @SimpleFunction(description = "Returns the ID of the specified component.")
+ public String GetId(Component component) {
+ if (component != null || COMPONENT_IDS.containsKey(component)) {
+ return COMPONENT_IDS.get(component);
+ }
- Object[] typed_params = parameters.toArray();
- Class>[] requested_params = method.getParameterTypes();
- ArrayList