How do i get component in java

I'm developing an extension to reduce blocks. I can't find any way to get the component by id. Like in android studio we can access any component by id. but i can't find any way to get component in java code.
Currently i'm passing components as a list to access component. If anyone knows how can i access component in java please help me

Provide sample code of what you are doing now. To give you a more precise answer.

package com.example.myextension;

import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.runtime.*;
import com.google.appinventor.components.common.*;

@DesignerComponent(
    version = 1,
    description = "An example extension",
    category = ComponentCategory.EXTENSION,
    nonVisible = true,
    iconName = "images/extension.png")
@SimpleObject(external = true)
public class MyExtension extends AndroidNonvisibleComponent {

    public MyExtension(ComponentContainer container) {
        super(container.$form());
    }

    @SimpleFunction(description = "Gets the text of a Label component by UUID")
    public String GetLabelText(String uuid) {
        // Assuming uuid is the UUID of the Label component
        for (Component component : form.getAllComponents()) {
            if (component.getUuid().equals(uuid)) {
                if (component instanceof Label) {
                    Label label = (Label) component;
                    return label.Text();
                }
            }
        }
        return "";
    }
}

i'm trying to get the component by ID or UUID. I thought the component name we assign to a component in Designer that is the component id. but it's not, so i tried to get by uuid and that didn't work

This

image

Label@ae55bd2

I believe, is the best you can hope for, and remember this is only at runtime, and it changes everytime the app is loaded/refreshed.

There isn't a direct way to do it, but there is a thread floating around that I wrote a while back that shows how to get the component by its name. I think a number of extension authors have used that logic but I'm failing to find it at the moment.

IIRC you made a test server

I did that too, but that was for a user-facing block that could do it (which extensions would also be able to use). The thread I am thinking of had Java code that worked with App Inventor at the time and still should.

Ok, I found the original discussion but misrecalled its intent. It was in the old forum and was around the issue of finding and calling procedures. However, the logic is very similar but the construction is different.

Thread for reference: https://groups.google.com/g/app-inventor-open-source-dev/c/NU4Qcj1PXwg/m/Ww0D-TN7AgAJ

Original code: Dropbox

Here's a subset of the code posted on Dropbox, but for finding components by name instead (not tested):

private Component lookupComponentInRepl(String componentName) {
  Scheme lang = Scheme.getInstance();
  try {
    // Since we're in the REPL, we can cheat and invoke the Scheme interpreter to get the method.
    Object result = lang.eval("(begin (require <com.google.youngandroid.runtime>)(get-component " +
        componentName + "))");
    if (result instanceof Component) {
      return (Component) result;
    } else {
      Log.e(LOG_TAG, "Wanted a Component, but got a " +
          (result == null ? "null" : result.getClass().toString()));
    }
  } catch (Throwable throwable) {
    throwable.printStackTrace();
  }
  return null;
}


private Component lookupComponentInForm(String componentName) {
  try {
    Field result = form.getClass().getField(componentName);
    if (result instanceof Component) {
      return (Component) result;
    } else {
      Log.e(LOG_TAG, "Wanted a Component, but got a " +
          (result == null ? "null" : result.getClass().toString()));
    }
  } catch (Throwable throwable) {
    throwable.printStackTrace();
  }
  return null;
}


private Component lookupComponent(String componentName) {
  if (form.isRepl()) {
    return lookupComponentInRepl(componentName);
  } else {
    return lookupComponentInForm(componentName);
  }
}
4 Likes

Thank you for your assistance. The code you provided is functioning perfectly.

I've made some modifications to the code. Here is the updated version.

package com.hridoy.getcomponentbyname;

import android.util.Log;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.Component;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.ReplForm;
import kawa.standard.Scheme;

import java.lang.reflect.Field;

public class GetComponentByName extends AndroidNonvisibleComponent {
  public String TAG = "GetComponentByName";

  public GetComponentByName(ComponentContainer container) {
    super(container.$form());
  }

  private Object lookupComponentInRepl(String componentName) {
    Scheme lang = Scheme.getInstance();
    try {
      // Since we're in the REPL, we can cheat and invoke the Scheme interpreter to get the method.
      Object result = lang.eval("(begin (require <com.google.youngandroid.runtime>)(get-component " +
              componentName + "))");
      if (result instanceof Component) {
        return (Component) result;
      } else {
        Log.e(TAG, "Wanted a Component, but got a " +
                (result == null ? "null" : result.getClass().toString()));
      }
    } catch (Throwable throwable) {
      throwable.printStackTrace();
    }
    return "";
  }


  private Object lookupComponentInForm(String componentName) {
    try {
      // Get the field by name
      Field field = form.getClass().getField(componentName);
      // Get the field's value, since field itself isn't a Component
      Object component = field.get(form);
      if (component instanceof Component) {
        return (Component) component;
      } else {
        Log.e(TAG, "Wanted a Component, but got a " +
                (component == null ? "null" : component.getClass().toString()));
      }
    } catch (NoSuchFieldException | IllegalAccessException e) {
      Log.e(TAG, "Error accessing component: " + componentName, e);
    }
    return "";
  }


  @SimpleFunction(description = "")
  public Object GetComponentByName(String componentName) {
    if (form instanceof ReplForm) {
      return lookupComponentInRepl(componentName);
    } else {
      return lookupComponentInForm(componentName);
    }
  }
}

AIA -
GetComponentTest.aia (7.6 KB)
AIX -
com.hridoy.getcomponentbyname.aix (5.0 KB)

image

:question:

1 Like

What is wrong?

You can get the component runtime value just by using the component block.

Extension needs to be around the other way - get name from runtime value

Actually i was just trying to get the component by its name programmatically so that i can acces the components when i use invoke in java code . Like i want to set property or call a function so i don't have to pass the component.

I'll be using this method to reduce block in big projects. It will help me in many ways. I'll write down a guide how to use this method later on. By following this method you can reduce hundreds of block.

You can use a component block. But using a string you can get something more dynamic than component block lists.

but you need to know what each component is named, so you would have to make a list, which kind of defeats the point.

When the names are identical, e.g. Label and differ only in digits, Label1, Label2, Label3 can be iterated through these labels without the need to build a list. It can be useful sometimes.

1 Like