Skip to content

Commit 0982c27

Browse files
committed
Public source released
0 parents  commit 0982c27

File tree

6 files changed

+282
-0
lines changed

6 files changed

+282
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.github/workflows/build.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
on: push
2+
name: Build my extension
3+
jobs:
4+
buildAIX:
5+
name: Build AIX
6+
runs-on: ubuntu-18.04
7+
steps:
8+
- uses: actions/checkout@master
9+
- name: Build AIX
10+
id: build_aix
11+
uses: ysfchn/appinventor-aix-action@master
12+
with:
13+
source: 'https://github.com/ysfchn/appinventor-sources.git'
14+
- name: Upload Artifact
15+
id: upload-artifact
16+
uses: actions/upload-artifact@v1.0.0
17+
with:
18+
name: ${{ steps.build_aix.outputs.file }}
19+
path: appinventor-sources/appinventor/components/build/extensions/${{ steps.build_aix.outputs.file }}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Yusuf Cihan
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# DynamicComponents-AI2
2+
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 App Inventor platform!
3+
4+
## Blocks
5+
6+
![](blockdescription.png)
7+
8+
Source code is licensed under MIT license.

blockdescription.png

151 KB
Loading
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package com.yusufcihan.DynamicComponents;
2+
3+
import com.google.appinventor.components.annotations.*;
4+
import com.google.appinventor.components.runtime.*;
5+
import com.google.appinventor.components.common.*;
6+
import com.google.appinventor.components.runtime.util.YailList;
7+
import com.google.appinventor.components.runtime.errors.YailRuntimeError;
8+
9+
import java.util.Hashtable;
10+
import java.util.Iterator;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
import java.lang.reflect.InvocationTargetException;
14+
import java.lang.reflect.Constructor;
15+
import java.lang.reflect.Method;
16+
import java.lang.reflect.Parameter;
17+
import java.lang.reflect.Modifier;
18+
import java.util.Arrays;
19+
import java.util.Set;
20+
import java.lang.Boolean;
21+
22+
import android.view.View;
23+
import android.view.ViewGroup;
24+
25+
@DesignerComponent(version = 3,
26+
description = "Dynamic Components extension to create any type of dynamic component in any arrangement.<br><br>- by Yusuf Cihan",
27+
category = ComponentCategory.EXTENSION,
28+
nonVisible = true,
29+
iconName = "https://yusufcihan.com/img/dynamiccomponents.png")
30+
@SimpleObject(external = true)
31+
public class DynamicComponents extends AndroidNonvisibleComponent implements Component {
32+
33+
// Variables
34+
private Hashtable<String, Object> COMPONENTS = new Hashtable<String, Object>();
35+
private List<String> blacklist = Arrays.asList("CopyHeight", "CopyWidth", "wait", "onClick", "Column", "Row", "setLastWidth", "setLastHeight");
36+
private String BASE_PACKAGE = "com.google.appinventor.components.runtime";
37+
private String LAST_ID = "";
38+
39+
public DynamicComponents(ComponentContainer container) {
40+
super(container.$form());
41+
}
42+
43+
private String BasePackage() {
44+
return BASE_PACKAGE;
45+
}
46+
47+
private void BasePackage(String packageName) {
48+
BASE_PACKAGE = packageName;
49+
}
50+
51+
// ------------------------
52+
// MAIN METHODS
53+
// ------------------------
54+
55+
@SimpleFunction(description = "Create a dynamic component that you want. It supports all components that added to App Inventor sources. Use 'in' parameter to specify the arrangement or canvas which new component will be placed in. Type the name of component in the 'componentName' section. (case sensitive) Use any component blocks to edit dynamic component's properties! ")
56+
public void Create(AndroidViewComponent in, String componentName, String id) {
57+
Object component = null;
58+
LAST_ID = id;
59+
// Check if id is used by another created dynamic component.
60+
if (!COMPONENTS.containsKey(id))
61+
{
62+
try
63+
{
64+
// Return the component class by looking the its name.
65+
Class clasz = Class.forName(BASE_PACKAGE + "." + componentName);
66+
// Create constructor object for creating a new instance.
67+
Constructor constructor = clasz.getConstructor(new Class[] { ComponentContainer.class });
68+
// Create a new instance of specified component.
69+
component = constructor.newInstance((ComponentContainer)in);
70+
}
71+
catch (Exception e)
72+
{
73+
// Throw a runtime error when something goes wrong.
74+
throw new YailRuntimeError(e.getMessage(),"Error");
75+
}
76+
COMPONENTS.put(id, component);
77+
}
78+
else
79+
{
80+
// Throw a runtime error when ID is already used for another component.
81+
throw new YailRuntimeError("This ID is already used, please pick another.","Duplicate ID");
82+
}
83+
84+
}
85+
86+
@SimpleFunction(description = "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.")
87+
public void Remove(String id) {
88+
Method m = null;
89+
Object cmp = null;
90+
// Don't do anything if id is not in the components list.
91+
if (COMPONENTS.containsKey(id) == false)
92+
return;
93+
94+
try
95+
{
96+
// Get the component.
97+
cmp = COMPONENTS.get(id);
98+
// Remove its id from components list.
99+
COMPONENTS.remove(id);
100+
// Hide the component.
101+
((AndroidViewComponent)cmp).Visible(false);
102+
}
103+
catch (Exception eh) { }
104+
}
105+
106+
@SimpleFunction(description = "Returns last used ID.")
107+
public String LastUsedID() {
108+
return LAST_ID;
109+
}
110+
111+
@SimpleFunction(description = "Returns the component's itself for setting properties. Component needs to be created with Create block. Type an ID which you typed in Create block to return the component.")
112+
public Object GetComponent(String id) {
113+
return COMPONENTS.get(id);
114+
}
115+
116+
@SimpleFunction(description = "Returns the component type name.")
117+
public String GetName(Object component) {
118+
return component.getClass().getName();
119+
}
120+
121+
@SimpleFunction(description = "Removes all created dynamic components. Same as Remove block, but for all created components.")
122+
public void RemoveAll() {
123+
Set<String> keys = COMPONENTS.keySet();
124+
for(String key: keys){
125+
Remove(key);
126+
}
127+
}
128+
129+
@SimpleFunction(description = "Get all available properties of a component which can be set from Designer as list along with types. Can be used to learn the properties of any component which is not static.")
130+
public YailList GetDesignerProperties(Object component) {
131+
// A list which includes designer properties.
132+
ArrayList names = new ArrayList();
133+
// Get the component's class and return all methods from it.
134+
Method[] methods = component.getClass().getMethods();
135+
for (Method mtd : methods)
136+
{
137+
// Read for @DesignerProperty annotations.
138+
// So we can learn which method is used as property setter/getter.
139+
if ((mtd.getDeclaredAnnotations().length == 2) && (mtd.isAnnotationPresent(DesignerProperty.class)))
140+
{
141+
// Get the DesignerProperty annotation.
142+
DesignerProperty n = mtd.getAnnotation(DesignerProperty.class);
143+
// Add editorType value and method name to the list.
144+
names.add(YailList.makeList(new String[] {
145+
mtd.getName(),
146+
n.editorType()
147+
}));
148+
}
149+
}
150+
// Return the list.
151+
return YailList.makeList(names);
152+
}
153+
154+
@SimpleFunction(description = "Set a property of a component by typing its name.")
155+
public void SetProperty(Object component, String propertyName, Object propertyValue) {
156+
// Read methods of the component.
157+
Method[] methods = component.getClass().getMethods();
158+
// The method will be invoked.
159+
Method method = null;
160+
// Class for casting purpose.
161+
Class caster = null;
162+
try
163+
{
164+
for (Method mtd : methods)
165+
{
166+
// Check for one parametered (setter) method.
167+
if((mtd.getName() == propertyName) && (mtd.getParameterCount() == 1))
168+
{
169+
// Save it for later.
170+
caster = mtd.getParameterTypes()[0];
171+
method = mtd;
172+
break;
173+
}
174+
}
175+
// Invoke the saved method.
176+
method.invoke(component, propertyValue);
177+
}
178+
catch (Exception eh)
179+
{
180+
// Throw an error when something goes wrong.
181+
throw new YailRuntimeError(eh.getMessage().toString(),"Error");
182+
}
183+
}
184+
185+
@SimpleFunction(description = "Get property value of a component.")
186+
public Object GetProperty(Object component, String propertyName) {
187+
// Read methods of the component.
188+
Method[] methods = component.getClass().getMethods();
189+
// The method will be invoked.
190+
Method method = null;
191+
try
192+
{
193+
for (Method mtd : methods)
194+
{
195+
// Check for zero parametered (getter) method.
196+
if((mtd.getName() == propertyName) && (mtd.getParameterCount() == 0))
197+
{
198+
// Save it for later.
199+
method = mtd;
200+
break;
201+
}
202+
}
203+
// Invoke the saved method and return its return value.
204+
return method.invoke(component);
205+
}
206+
catch (Exception eh)
207+
{
208+
// Throw an error when something goes wrong.
209+
throw new YailRuntimeError(eh.getMessage().toString(),"Error");
210+
}
211+
}
212+
213+
@SimpleFunction(description = "Returns the ID of component. Component needs to be created by Create block. Otherwise it will return -1.")
214+
public String GetId(Object component) {
215+
return getKeyFromValue(COMPONENTS, component);
216+
}
217+
218+
// ------------------------
219+
// PRIVATE METHODS
220+
// ------------------------
221+
222+
// Getting key from value, found on:
223+
// http://www.java2s.com/Code/Java/Collections-Data-Structure/GetakeyfromvaluewithanHashMap.htm
224+
public String getKeyFromValue(Hashtable hm, Object value) {
225+
for (Object o : hm.keySet()) {
226+
if (hm.get(o).equals(value)) {
227+
return (String)o;
228+
}
229+
}
230+
return "";
231+
}
232+
}

0 commit comments

Comments
 (0)