@ewpatton sir kindly have a look.
I did test it again, the component
object is always null and produces and error with crash.
pls explain me i cant understand the above codes
@vknow360 I did some research and ended up failing with this code
static class EventComponentContainer implements ComponentContainer {
public static EventForm form;
public static ExtendedAct activity;
public EventComponentContainer(){
form = new EventForm();
activity = new ExtendedAct();
}
@Override
public Activity $context() {
return activity;
}
@Override
public Form $form() {
return form.$form();
}
@Override
public void $add(AndroidViewComponent component) {
}
@Override
public void setChildWidth(AndroidViewComponent component, int width) {
}
@Override
public void setChildHeight(AndroidViewComponent component, int height) {
}
@Override
public int Width() {
return 1;
}
@Override
public int Height() {
return 1;
}
class EventForm extends Form {
@Override
public boolean canDispatchEvent(Component component, String str) {
return true;
}
@Override
public boolean dispatchEvent(Component component, String str, String str2, Object[] objArr) {
getDispatchDelegate().dispatchEvent(component,str,str2,objArr);
Log.e("EventDispatchedListener", str);
return true;
}
@Override
public void dispatchGenericEvent(Component component, String str, boolean z, Object[] objArr) {
getDispatchDelegate().dispatchGenericEvent(component,str,z,objArr);
Log.e("EventDispatchedListener", str);
}
public void context(Context context) {
attachBaseContext(context);
}
}
class ExtendedAct extends Activity {
public void context(Context context) {
attachBaseContext(context);
Log.e("Notification", "Attached");
}
}
}
If I pass this extended component container to the component constructor then the component is null. Changing $context
and $form
value to the default values, there is no error. I have still found the solution and reason why the component is null. From today morning I am searching for listening to all the method invokes in a component class. I did find a similar question asked in StackOverFlow and it was not really helpful:
:(
It would be really great and helpful if you say something about this topic if it's possible or not @ewpatton.
what i want is i register a component i need to find when will all the event in a component occurs like
public void EventOccured(Component com, String eventName, List variableNames, List variable Values)
Is there any update on this? Is it possible to listen for event dispatches in a component? What's the conclusion?
This sounds good, but does it work?
Are you trying to listen for dispatches on an existing component or for one you've created dynamically?
I am trying to listen for dispatches on components created dynamically. I had tried serval times to create a new ComponentContainer but the Component
object created is null and that crashes the app.
I think it might be possible but it is definitely non-trivial. I'll have to think about it. I may have time to explore this a bit on Wednesday.
thanks for looking into it!!
it does not work i just told that i need function like that if they can
I think you can only override event dispatches when you override the form.
can you give me an example?
Here is an example that I wrote:
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright © 2021 MIT, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package edu.mit.appinventor.extensions.event_example;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.Component;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.OnClearListener;
import gnu.mapping.Environment;
import gnu.mapping.ProcedureN;
import gnu.mapping.SimpleSymbol;
import gnu.mapping.Symbol;
import gnu.mapping.Values;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The DynamicComponents extension demonstrates how to create a component at runtime and attach
* event listeners.
*
* @author Evan W. Patton (ewpatton@mit.edu)
*/
@DesignerComponent(version = 1, nonVisible = true, category = ComponentCategory.EXTENSION)
@SimpleObject(external = true)
public class DynamicComponents extends AndroidNonvisibleComponent implements OnClearListener {
/**
* A mapping of component names to the component instances they represent.
*/
private final Map<String, Component> components = new HashMap<>();
/**
* A mapping of qualified event name symbols to the implementation of the corresponding handler.
*/
private final Map<Symbol, EventHandler> eventHandlers = new HashMap<>();
/**
* The {@code EventHandler} class provides the basis for dispatching an event. In the normal
* operation of App Inventor, the Scheme code would generate a lambda function that would be
* invoked during the event handling.
*/
class EventHandler extends ProcedureN {
/**
* The component to which the event handler is bound.
*/
private final Component component;
/**
* The name of the event to which this handler corresponds.
*/
private final String name;
EventHandler(Component component, String name) {
this.component = component;
this.name = name;
}
@Override
public Object applyN(Object[] objects) {
EventFired(component, name, Arrays.asList(objects));
return Values.empty;
}
}
public DynamicComponents(Form form) {
super(form);
}
///region Methods
/**
* Creates a new component with the given name of the given type.
*
* @param type the type for the component, either an unqualified name in the App Inventor runtime
* or a fully qualified name for an extension.
* @param name the name of the component, as the user might call it.
* @return an instance of the component type, if the type exists and is a valid component type.
*/
@SimpleFunction
public Component CreateComponent(String type, String name) {
if (components.containsKey(name)) {
return components.get(name);
}
if (type.indexOf('.') == -1) {
type = "com.google.appinventor.components.runtime." + type;
}
Component result = makeComponent(type);
getFormEnvironment().put(new SimpleSymbol(name), result);
components.put(name, result);
return result;
}
/**
* Registers a handler for the given component name and the given event name. Returns true if
* the event was registered successfully, otherwise false.
*
* @param componentName the component name
* @param eventName the event name
* @return true if the event was registered successfully, otherwise false.
*/
@SimpleFunction
public boolean RegisterForEvent(String componentName, String eventName) {
Component component = components.get(componentName);
if (component == null) {
return false;
}
Symbol eventSymbol = SimpleSymbol.valueOf(componentName + "$" + eventName);
if (eventHandlers.containsKey(eventSymbol)) {
return false;
}
Environment environment = getFormEnvironment();
EventHandler h = new EventHandler(component, eventName);
eventHandlers.put(eventSymbol, h);
environment.put(eventSymbol, h);
EventDispatcher.registerEventForDelegation(form, componentName, eventName);
return true;
}
///endregion
///region Events
/**
* The EventFired event is raised when a registered event handler fires of the given event name
* for the given component previously registered with RegisterForEvent.
*
* @param component the component that raised the event
* @param event the name of the event raised
* @param arguments any additional arguments related to the event. this will always be a list,
* but the contents will vary from event to event.
*/
@SimpleEvent
public void EventFired(Component component, String event, List<Object> arguments) {
EventDispatcher.dispatchEvent(this, "EventFired", component, event, arguments);
}
///endregion
///region OnClearListener implementation
@Override
public void onClear() {
components.clear();
}
///endregion
///region Utilities
/**
* Utility function to create a new component of the given type.
*
* @param type the type of the component to create
* @return an instance of the component type
* @throws IllegalArgumentException if type does not implement Component
* @throws IllegalStateException if the type does not represent a Java Class object
*/
private Component makeComponent(String type) {
try {
Class<?> t = Class.forName(type);
if (!Component.class.isAssignableFrom(t)) {
throw new IllegalArgumentException(type + " does not correspond to a Component type");
}
for (Constructor<?> constructor : t.getConstructors()) {
Class<?>[] parameters = constructor.getParameterTypes();
if (parameters.length == 1 && parameters[0].isAssignableFrom(Form.class)) {
return (Component) constructor.newInstance(form);
}
}
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Unable to instantiate component of type " + type, e);
}
throw new IllegalStateException("Unable to instantiate component of type " + type);
}
/**
* Gets the form environment via reflection.
*
* @return the form environment
* @throws IllegalStateException if the form's environment cannot be retrieved via reflection
*/
private Environment getFormEnvironment() {
try {
Field f = form.getClass().getField("form$Mnenvironment");
return (Environment) f.get(form);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Unable to get form environment", e);
}
}
///endregion
}
And here is an example of how you could create a Clock and listen for its Timer event:
Note that I've only tested this in the companion. In theory it should work in compiled apps as well but I make no guarantees.
Really thanks @ewpatton! I am very exited to test it! Can you please post the project file if possible?
It works perfect! Really thanks for spending time to look into it! This was what I was waiting for!
This works in the compiled app too!
This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.