For the last few days, I'm trying to create a Component container for components to create through Reflection. I couldn't.
How do you actually create a working (implemented
) class of it?
The component container needs to Override things like $context
and $form
. I tried to create an extended class of all of them (Form
and activity
).
My code
class FForm extends Form {
@Override
public boolean canDispatchEvent(Component component, String str) {
return true;
}
@Override
public boolean dispatchEvent(Component component, String str, String str2, Object[] objArr) {
Log.d(TAG, "Dispatch event: " + component.toString() + ", values: " + Arrays.toString(objArr) + ", str one: " + str + ", str two: " + str2);
getDispatchDelegate().dispatchEvent(component,str,str2,objArr);
return true;
}
@Override
public void dispatchGenericEvent(Component component, String str, boolean z, Object[] objArr) {
Log.d(TAG, "Dispatch Generic Event: " + component.toString() + ", values: " + Arrays.toString(objArr) + ", str one: " + str + ", boolean two: " + z);
getDispatchDelegate().dispatchGenericEvent(component,str,z,objArr);
}
@Override
public void onDestroy() {
}
@Override
public void onPause() {
}
}
static class AActivity extends Activity {
public void init(Context context) {
attachBaseContext(context);
}
@Override
public String getLocalClassName() {
return "Screen1";
}
}
static class CComponentContainer implements ComponentContainer {
private final BackgroundService service;
public CComponentContainer(BackgroundService service){
this.service = service;
}
@Override
public Activity $context() {
return service.activity;
}
@Override
public Form $form() {
return service.form;
}
@Override
public void $add(AndroidViewComponent androidViewComponent) {
}
@Override
public void setChildWidth(AndroidViewComponent androidViewComponent, int i) {
}
@Override
public void setChildHeight(AndroidViewComponent androidViewComponent, int i) {
}
@Override
public int Width() {
return 1;
}
@Override
public int Height() {
return 1;
}
}
This is how I initialize it:
activity = new AActivity();
activity.init(getApplicationContext());
form = new FForm();
container = new CComponentContainer(this);
(some code is from my old extension)
I supply the container
to the component as the constructor.
For some components it does work and for some, it doesn't.
Like, the LightSensor
component. The component(s) failes to initialize and the error occurs.
This is the error of the component:
Caused by: java.lang.IllegalStateException: System services not available to Activities before onCreate()
Also, I'm trying to get the Form environment of it using the extended Form
which is FForm
in the code above.
java.lang.RuntimeException: Unable to destroy activity {appinventor.ai_bgpsdr.LocationDataSensor/appinventor.ai_bgpsdr.LocationDataSensor.Screen1}: java.lang.IllegalStateException: Unable to get form
I found these issues with the snippets that you have shared.
- Form is a subclass of Activity, so you should implement ComponentContainer interface there itself.
- Instantiating Activity doesn't work. Android requires an entry in the manifest.
- For non-visible components, the ComponentContainer is the screen or the Form class.
This means that you should call super.onCreate()
beforehand.
2 Likes
I am consused do I need to implement ComponentContainer in Activity itself?
Now I've added the activity element and now the error (Caused by: java.lang.IllegalStateException: System services not available to Activities before onCreate()
) seems to be gone.
So can I try to assign Form as the constructor for the Component object?
You should do something like this as you want to create a ComponentContainer
instance at runtime
container = new ComponentContainer {
... // implement methods
}
If you are creating this within a component/extension, you could use the component's $activity
and $form
references to implement the ComponentContainer
interface.
Otherwise, currently active Form
can be accessed using Form.getActiveForm()
and the same can be used as Activity
.
Yes, as Form
is a ComponentContainer
at the root level.
1 Like
Thank you for your help Pavitra ![:slightly_smiling_face: :slightly_smiling_face:](https://community.appinventor.mit.edu/images/emoji/google/slightly_smiling_face.png?v=9)
2 Likes
Hello Pavitra, I was able to create most of the components successfully but for components like Player
however it did work work. As the player uses Form, it crashes at this line:
form.setVolumeControlStream(AudioManager.STREAM_MUSIC);
Source: https://github.com/mit-cml/appinventor-sources/blob/ffba5beaca12b9b443344d02e2241df8bdc27236/appinventor/components/src/com/google/appinventor/components/runtime/Player.java#L148
Do you know why does it happen? The form
is null according to the app logs:
06-21 21:49:38.072 15310 15433 W System.err: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.Window.setVolumeControlStream(int)' on a null object reference
06-21 21:49:38.073 15310 15433 W System.err: at android.app.Activity.setVolumeControlStream(Activity.java:6288)
06-21 21:49:38.073 15310 15433 W System.err: at com.google.appinventor.components.runtime.Player.<init>(Player.java:148)
I also attach the base context for the form
as did with activity
.
1 Like
Seems like the Window
is null and not the form
. How do I get the main window and override it?
https://developer.android.com/reference/android/app/Activity#getWindow()
It could be because current Activity
's onCreate()
may not have been called yet.
Could you show the full stacktrace?
Here is the full stack trace:
06-22 09:08:31.621 14913 14945 W System.err: java.lang.reflect.InvocationTargetException
06-22 09:08:31.622 14913 14945 W System.err: at java.lang.reflect.Constructor.newInstance0(Native Method)
06-22 09:08:31.622 14913 14945 W System.err: at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
06-22 09:08:31.622 14913 14945 W System.err: at com.kumaraswamy.tasks.ActivityService.createComponent(ActivityService.java:259)
06-22 09:08:31.622 14913 14945 W System.err: at com.kumaraswamy.tasks.ActivityService.processComponentList(ActivityService.java:145)
06-22 09:08:31.622 14913 14945 W System.err: at com.kumaraswamy.tasks.ActivityService.processTasks(ActivityService.java:91)
06-22 09:08:31.622 14913 14945 W System.err: at com.kumaraswamy.tasks.ActivityService.access$000(ActivityService.java:34)
06-22 09:08:31.622 14913 14945 W System.err: at com.kumaraswamy.tasks.ActivityService$1$1.run(ActivityService.java:74)
06-22 09:08:31.622 14913 14945 W System.err: at java.util.TimerThread.mainLoop(Timer.java:562)
06-22 09:08:31.622 14913 14945 W System.err: at java.util.TimerThread.run(Timer.java:512)
06-22 09:08:31.626 14913 14945 W System.err: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.Window.setVolumeControlStream(int)' on a null object reference
06-22 09:08:31.626 14913 14945 W System.err: at android.app.Activity.setVolumeControlStream(Activity.java:6288)
06-22 09:08:31.626 14913 14945 W System.err: at com.google.appinventor.components.runtime.Player.<init>(Player.java:148)
06-22 09:08:31.626 14913 14945 W System.err: ... 9 more