Access object property by string in Java/Android?

Discussion in 'Programming & Software Development' started by Athiril, Sep 8, 2020.

  1. Athiril

    Athiril Member

    Joined:
    Jun 11, 2003
    Messages:
    4,114
    Location:
    Tsumagoi-Mura, Japan
    I am using Android Studio and I have an object with properties, lets say myobject.

    myobject has a property called myproperty, normally I can access it by myobject.myproperty

    But in this case I need to refer to it by string.

    So I would need to access it by something like
    String a = "myproperty";
    if (myobject[a] != null){
    //stuff
    }

    But that isnt working here, and I dont know why I cant figure it out.
     
  2. theSeekerr

    theSeekerr Member

    Joined:
    Jan 19, 2010
    Messages:
    3,530
    Location:
    Broadview SA
  3. OP
    OP
    Athiril

    Athiril Member

    Joined:
    Jun 11, 2003
    Messages:
    4,114
    Location:
    Tsumagoi-Mura, Japan
    There is a method provided by I havent been able to get it to work

    public T get (Key<T> key)

    I want to get the value of a property into a string, from a reference to that property by a string. Also getDeclaredField isnt a method of this class I am using (with import java.lang.reflect.*; )

    Unless I am missing something, the example link you have given shows how to get the property name. I need to get the property value and not the. I already have the property names by the following:

    for (CameraCharacteristics.Key <?> key : characteristics.getKeys()) {
    mystring.append(key.getName() + "\n");
    }
     
    Last edited: Sep 8, 2020
  4. Yehat

    Yehat Member

    Joined:
    Aug 4, 2002
    Messages:
    467
    Location:
    Melbourne
    Prefix this by saying I've not used Android Studio before, just downloaded it out of curiosity because of your post and have been playing around with the mobile phone emulators which from first impression are really cool :) But I digress...

    So to call the above definition of get() we need to provide a corresponding Key object.
    According to the docs:
    Source: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.Key

    As suggested above we can use either a predefined key(preferred), or we can create our own.

    1) Predefined keys:
    As per your code example, there are a bunch of predefined keys exposed in the Class CameraCharacteristics: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics
    When you look at their definitions, you will see in addition to names they have a variety of Types associated with them (more on this in a minute but I suspect this is the source of your problem?).

    So if we do this using the code you've posted, iterating through the Keys we can just call get() on them directly:

    (#1a)
    Code:
    for (CameraCharacteristics.Key <?> key : characteristics.getKeys())
    {
       Object value = characteristics.get(key);
       if(value != null)
       {
       // blah
       }
    }
    
    ----
    Or if we want to lookup a specific key:

    (#1b)
    Code:
    CameraCharacteristics.Key myKey = CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE;
    Object myValue = characteristics.get(myKey);
    if (myValue != null)
    {
       // blah
    }
    
    I'm assuming you already know this nothing new here.

    2) Create your own Key
    I'm assuming what you want though, instead of #1b where we are referencing a predefined key directly like:
    Code:
    CameraCharacteristics.Key myKey = CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE;
    
    You instead have String containing the corresponding Key Name, and want use get() on it ?

    Using the same example in (#1b) the Name of the corresponding Key in CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE resolves to "android.control.aeLockAvailable"

    So:
    Code:
    String myKeyName = "android.control.aeLockAvailable";
    characteristics.get(myKeyName); // or some variant of this syntax is failing  ?
    
    Are you trying to do something like the last line above (or some syntax variant of it with generics) and are stuck ?

    If so, as declared, get() takes a "Key", which if we look at the Constructor definition, the Key needs *two* values to instantiate.
    Code:
    Key(String name, Class<T> type)
    Construct a new Key with a given name and type.
    
    I'd mentioned Type before, given that the Key needs two values (especially the Type) is that the problem you're having in being able to use get() in this way?

    If so, we just need to pass the expected Type as well, in this case we know that the CONTROL_AE_LOCK_AVAILABLE has an associated type of Boolean (from the API docs https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#getKeys()), so for our call to get() we can construct a key like:

    Code:
    String myKeyName = "android.control.aeLockAvailable";
    CameraCharacteristics.Key aKey = new CameraCharacteristics.Key(myKeyName , Boolean.TYPE);
    Object retVal = characteristics.get(aKey);
    
    if(retVal != null)
    {
    // do stuff
    }
    
    Obviously that's just an example because there's already a predefined key for the property we are looking up, we wouldn't create a new key.

    -----
    If you're then going to ask that instead of a fixed String in myKeyName say we had:
    If myKeyName was set to some dynamic value then "How would we be able to call get() in this case, because we'd need to be able to convert the String in myKeyName into a corresponding Key object". That could involve either:
    -Mapping myKeyName to a predefined Key object or
    -Being able to create a new Key where you need to somehow determine the Type argument to the Key's constructor (as per the API docs it's not always just going to be Boolean.Type as our example in #2).

    Some possible suggestions for the former (just thinking out loud):
    a) If it's a predefined key you're looking for, you could call getKeys() - https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#getKeys() and search the returned objects to find a predefined Key which matches the Name you are looking for, then pass that predefined Key to get().
    b) If you're going to do (a) a lot, instead you could iterate through the list of getKeys() once, and create a map of KeyName to Type (or even KeyName to Key actually) for the Keys of interest to you, which you could use in subsequent lookups to go from your String Name to a Key. This is so you don't have to iterate through the collection each time (like in a)
    c) people more familiar with android/java may be able to assist with additional options, and/or involve the reflection API like you've already looked at.
     
    Last edited: Sep 10, 2020
  5. OP
    OP
    Athiril

    Athiril Member

    Joined:
    Jun 11, 2003
    Messages:
    4,114
    Location:
    Tsumagoi-Mura, Japan
    This is exactly what I need to do, I tried and it didnt work and thought I was losing it.

    Here is the problem, when I try to do that I get
    "'Key(java.lang.String, java.lang.Class<T>)' is not public in 'android.hardware.camera2.CameraCharacteristics.Key'. Cannot be accessed from outside package", I dont know why this is happening

    In the documentation Key is
    Code:
    public static final class CameraCharacteristics.Key
    
    As you said, I was doing it via a loop of getKeys and getAvailableCaptureRequestKeys, setting a key was even more of a pain, so I had a work around where I have some strings
    Code:
    String mykey = "the.key.I.want";
    byte[] byteEnable = new byte[]{1};
    CaptureRequest<byte[]> reusablekey;
    for (CaptureRequest.Key <?> Key : characteristics.getAvailableCaptureRequestKeys()){
    check = key.getName();
    if (check.contains(mykey)){
    reusablekey = (CaptureRequest.Key<byte[]>) Key;
    stillBuilder.set(reusablekey, byteEnable); //stillBuilder a previously built CaptureRequest.Builder
    }
    }
    I have now run into problems where not only do I have not predefined keys, and additional vendor keys which dont show up on the iterating through the key list, which I know are there now.

    com.xiaomi.qcfa.supported is available via the list but

    org.codeaurora.qcamera3.quadra_cfa.activeArraySize
    org.codeaurora.qcamera3.quadra_cfa.availableStreamConfigurations
    org.codeaurora.qcamera3.quadra_cfa.is_qcfa_sensor

    Are not on the iterated list, but are there.

    activeArraySize is a rect
    is_qcfa_sensor is a byte
    an the availStreamConfigurations is a StreamConfiguration from camera2
     
    Last edited: Sep 10, 2020
  6. Yehat

    Yehat Member

    Joined:
    Aug 4, 2002
    Messages:
    467
    Location:
    Melbourne
    Off the top, I think your problem is likely beyond what I can help with, as I said it's my first time using Android Studio :)

    However, what I can help with is the code I posted did work for me, in that I don't get this issue:
    I'll provide the steps I used to get it working and full code if you want to compare against your build. What I did was:

    In the MyFirstApp project I made 2 changes.
    1) Modified: com.example.myfirstapp.MainActivity and rigged up enough code to be able to replicate your loop code (the way I've just fabricated these objects in the one class is in no way meant to be indicative of what proper code would be, but I just wanted enough pre-requisite objects to be able to run your initial loop code)

    Code:
    package com.example.myfirstapp;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.content.Context;
    import android.hardware.camera2.CameraCharacteristics;
    import android.hardware.camera2.CameraManager;
    import android.os.Bundle;
    
    
    public class MainActivity extends AppCompatActivity {
    
        // Static Method Test
        public static CameraCharacteristics.Key<Byte> IS_SUPPORT_QCFA_SENSOR =
                new CameraCharacteristics.Key<>("org.codeaurora.qcamera3.quadra_cfa.is_qcfa_sensor", Byte.class);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            Context applicationContext = getApplicationContext();
            CameraManager manager = (CameraManager)applicationContext.getSystemService(CAMERA_SERVICE);
            try {
                CameraCharacteristics characteristics = null;
                if(manager.getCameraIdList().length > 0)
                {
                    characteristics = manager.getCameraCharacteristics((manager.getCameraIdList())[0]);
    
                    StringBuffer mystring = new StringBuffer();
                    for (CameraCharacteristics.Key <?> key : characteristics.getKeys()) {
                        mystring.append(key.getName() + "\n");
                    }
                    System.out.println("### Printing Camera Characteristic Key Names [START]");
                    System.out.println(mystring.toString());
                    System.out.println("### Camera Characteristic Key Names [END] ");
    
                    String myKeyName = "android.control.aeLockAvailable";
                    CameraCharacteristics.Key aKey = new CameraCharacteristics.Key<>(myKeyName , Boolean.TYPE);
                    Object retVal = characteristics.get(aKey);
    
                    if(retVal != null)
                    {
                        Boolean bValue = (Boolean) retVal;
                        System.out.println("### Got a returned Boolean for android.control.aeLockAvailable, which was: "+bValue.toString());
                    }
    
                    // Static Method Test:
                    CameraCharacteristics.Key<Byte> myCustomCharacteristic = MainActivity.IS_SUPPORT_QCFA_SENSOR;
                    System.out.println("### myCustomCharacteristic has a name of: " +myCustomCharacteristic.getName());
                }
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
                // doSomethingMeaningful();
            }
            setContentView(R.layout.activity_main);
        }
    }
    
    2) In gradle scripts --> build.gradle - I set the:
    minSdkVersion 30
    targetSdkVersion 30

    Because the default MinSdk version was 16 and certain calls above were generating compiler messages that they needed later versions (latest being 29). Since the target env for me was 30 anyway, I set the min to the same (I think another way would've been to use a Suppression directive )

    Relevant Output when running the above in Debug Mode (I can also set breakpoints to inspect objects to see the values being returned):
    Code:
    I/System.out: ### Printing Camera Characteristic Key Names [START]
        android.colorCorrection.availableAberrationModes
        android.control.aeAvailableAntibandingModes
        android.control.aeAvailableModes
        android.control.aeAvailableTargetFpsRanges
        android.control.aeCompensationRange
        android.control.aeCompensationStep
    I/System.out: android.control.aeLockAvailable
        android.control.afAvailableModes
        android.control.availableEffects
        android.control.availableModes
        android.control.availableSceneModes
        android.control.availableVideoStabilizationModes
        android.control.awbAvailableModes
        android.control.awbLockAvailable
        android.control.maxRegionsAe
        android.control.maxRegionsAf
        android.control.maxRegionsAwb
        android.control.zoomRatioRange
        android.edge.availableEdgeModes
        android.flash.info.available
        android.info.supportedHardwareLevel
        android.jpeg.availableThumbnailSizes
        android.lens.facing
        android.lens.info.availableApertures
        android.lens.info.availableFilterDensities
        android.lens.info.availableFocalLengths
        android.lens.info.availableOpticalStabilization
        android.lens.info.focusDistanceCalibration
        android.noiseReduction.availableNoiseReductionModes
        android.request.availableCapabilities
        android.request.maxNumOutputProc
        android.request.maxNumOutputProcStalling
        android.request.maxNumOutputRaw
        android.request.partialResultCount
        android.request.pipelineMaxDepth
        android.scaler.availableMaxDigitalZoom
        android.scaler.croppingType
        android.scaler.mandatoryStreamCombinations
        android.scaler.streamConfigurationMap
    I/System.out: android.sensor.availableTestPatternModes
        android.sensor.info.activeArraySize
        android.sensor.info.physicalSize
        android.sensor.info.pixelArraySize
        android.sensor.info.preCorrectionActiveArraySize
        android.sensor.info.timestampSource
        android.sensor.orientation
        android.shading.availableModes
        android.statistics.info.availableFaceDetectModes
        android.statistics.info.availableLensShadingMapModes
        android.statistics.info.maxFaceCount
        android.sync.maxLatency
        ### Camera Characteristic Key Names [END]
        ### Got a returned Boolean for android.control.aeLockAvailable, which was: false
        ### myCustomCharacteristic has a name of: org.codeaurora.qcamera3.quadra_cfa.is_qcfa_sensor
    
    As above, the 2nd last line of output (based on building a Key from a String Name) completed without errors.

    Edit:
    There's a similar post on Stack Overflow to this problem (which might be yours?? at the time of writing no replies) https://stackoverflow.com/questions...ide-package-camera2-cameracharacteristics-key

    If so the code in that example, I've added to the above class (as "Static Method Test") and it runs without the error in the above post.

    Probably stating the obvious, but if you're building on a local PC, have you tried a Build -> Clean Project & Rebuild Project ?
     
    Last edited: Sep 10, 2020

Share This Page

Advertisement: