Java Question B4A 6 beta - How to add aar in Eclipse-project

DonManfred

Expert
Licensed User
Longtime User
How do i add support for aar files in Eclipse when i write a wrapper for a library?

Otherwise; if it is better to use android studio: How do i need to start a new project to wrap a third party library with android studio but i want to compile the wrapper with SLC?

Any hints on this?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Assuming that you have an aar file:

1. Open the aar file with 7zip and extract classes.jar.
2. Put this jar in the libs folder.
3. In the @DependsOn annotation you should write: @DependsOn(values={"NameOfTheAAR.aar"})

Your library will be made of 3 files:
<your library>.xml
<your library>.jar
<aar file>.aar

The advantage of aar libraries is that assets and resources are added automatically.
 

DonManfred

Expert
Licensed User
Longtime User
1. Open the aar file with 7zip and extract classes.jar.
2. Put this jar in the libs folder.
3. In the @DependsOn annotation you should write: @DependsOn(values={"NameOfTheAAR.aar"})
i understand... the added classes.jar will help with the imports as it is the jar i usually would add...

Thank you. I´m sure i´ll have a first test soon :)
 

corwin42

Expert
Licensed User
Longtime User
1. Open the aar file with 7zip and extract classes.jar.
2. Put this jar in the libs folder.
3. In the @DependsOn annotation you should write: @DependsOn(values={"NameOfTheAAR.aar"})

Will it be the same for libraries which use Google Play services and the support library?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Will it be the same for libraries which use Google Play services and the support library?
Start with my answer here: https://www.b4x.com/android/forum/t...to-google-play-services-fused-location.67748/

Example of library that depends on Firebase:
B4X:
package anywheresoftware.b4a.objects;

import java.io.Serializable;
import java.util.Map.Entry;

import android.os.Bundle;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.DependsOn;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.objects.collections.Map;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.firebase.analytics.FirebaseAnalytics;

@ShortName("FirebaseAnalytics")
@Version(1.0f)
@DependsOn(values={"com.google.firebase:firebase-crash", "com.google.firebase:firebase-analytics"})
public class FirebaseAnalyticsWrapper extends AbsObjectWrapper<FirebaseAnalytics>{
   /**
    * Initializes the object. FirebaseAnalytics should be a process global variable in the Starter service. It should be initialized in Service_Create sub.
    */
   public void Initialize() {
     setObject(FirebaseAnalytics.getInstance(BA.applicationContext));
   }
   /**
    * Sends an event to the analytics service.
    *EventName - Event name.
    *Parameters - Map of parameters. Pass Null if not needed.
    */
   public void SendEvent(String EventName, Map Parameters) {
     Bundle bundle = new Bundle();
     if (Parameters != null && Parameters.IsInitialized()) {
       for (Entry<Object, Object> e : Parameters.getObject().entrySet()) {
         bundle.putSerializable(String.valueOf(e.getKey()), (Serializable)e.getValue());
       }
     }
     getObject().logEvent(EventName, bundle);
   }
   /**
    * Tests whether Google Play Services are avaialable on the device.
    */
   public boolean getIsGooglePlayServicesAvailable() {
     return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(BA.applicationContext) == ConnectionResult.SUCCESS;
   }
}

It is similar to how you need to reference Google Play services. Secondary dependencies will be added automatically.
List of services: https://developers.google.com/android/guides/setup#add_google_play_services_to_your_project
Note that the version should not be included.

During compilation you need to reference a jar. You can use B4A to create this jar.

Create a new project and add the dependencies with #AdditionalJar.
For example:
B4X:
#AdditionalJar: com.google.android.gms:play-services-drive

You will now find the jar with all the relevant classes under Objects\bin\extra. You can take opt.jar or stan.jar. They are the same.
 

corwin42

Expert
Licensed User
Longtime User
Note that the version should not be included.

Can the version be included?
This would be interesting for the support library. In the past Google released some versions of the support library which were buggy like hell. Would be nice if we could go back to an older version.

During compilation you need to reference a jar. You can use B4A to create this jar.

Create a new project and add the dependencies with #AdditionalJar.
For example:
B4X:
#AdditionalJar: com.google.android.gms:play-services-drive

You will now find the jar with all the relevant classes under Objects\bin\extra. You can take opt.jar or stan.jar. They are the same.

That's a nice feature.

I think I understand now and will try to update a first simple library to the new system (Perhaps AHViewPager because it only uses the v4 support library which should be simple).

One question: If I have a library that uses the old .jar file in @DependsOn and another library which uses the same library from the maven repository in @DependsOn I guess only the maven library will be used?

The biggest advantage of the new method seems to be that you won't have to add all these resources with #AdditionalRes attribute. Makes things much easier for the developers.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The biggest advantage of the new method seems to be that you won't have to add all these resources with #AdditionalRes attribute
The biggest advantage is that you can use the new libraries such as Firebase and Google Play Services. They are no longer distributed as a single package.

The version will not be added for now.
If I have a library that uses the old .jar file in @DependsOn and another library which uses the same library from the maven repository in @DependsOn I guess only the maven library will be used?
If there is at least one reference to a maven library then references to google-play-services.jar and android-support-v4.jar will be ignored.
 

DonManfred

Expert
Licensed User
Longtime User
Can compile with SCL?
yes

This is the code for my Remoteconfig wrap

B4X:
package de.donmanfred;

import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;

import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.DependsOn;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.objects.collections.Map;

@Version(1.01f)
@ShortName("RemoteConfig")
@Author(value = "DonManfred (wrapper)")
//@Permissions(values={"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE"})
@Events(values={"onFetchComplete(success As Boolean)"})
@DependsOn(values={"com.google.firebase:firebase-config","com.google.firebase:firebase-core"})
//@DesignerProperties(values = {
//        @Property(key="Checked", displayName="Checked", defaultValue="False", fieldType="Boolean")
//    })
public class FirebaseRemoteConfigWrapper extends AbsObjectWrapper<FirebaseRemoteConfig> {
    private BA ba;
    private String eventName;

    public static final boolean DEFAULT_VALUE_FOR_BOOLEAN = false;
    public static final double DEFAULT_VALUE_FOR_DOUBLE = 0.0;
    public static final long DEFAULT_VALUE_FOR_LONG = 0;
    public static final String DEFAULT_VALUE_FOR_STRING = "";
    public static final int LAST_FETCH_STATUS_FAILURE = 1;
    public static final int LAST_FETCH_STATUS_NO_FETCH_YET = 0;
    public static final int LAST_FETCH_STATUS_SUCCESS = -1;
    public static final int LAST_FETCH_STATUS_THROTTLED = 2;
    public static final int VALUE_SOURCE_DEFAULT = 1;
    public static final int VALUE_SOURCE_REMOTE = 2;
    public static final int VALUE_SOURCE_STATIC = 0;
   
   
    public void Initialize(final BA ba, String EventName) {
        this.eventName = EventName.toLowerCase(BA.cul);
        this.ba = ba;

        FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
        final String eventName = EventName.toLowerCase(BA.cul);
        setObject(mFirebaseRemoteConfig);
        //getObject().fetch(cacheExpirationSeconds)
   
    }
    /**
     * Fetches parameter values for your app. Configuration values may be served
     * from the Default Config (local cache) or from the Remote Config Server,
     * depending on how much time has elapsed since parameter values were last
     * fetched from the Remote Config server. This method lets the caller
     * specify the cache expiration in seconds.
     * @return
     */
//    public Task<Void> fetch (long cacheExpirationSeconds){
//        return getObject().fetch(cacheExpirationSeconds);
//    }

    /**
     * Fetches parameter values for your app. Parameter values may be from the
     * Default Config (local cache), or from the Remote Config Server,
     * depending on how much time has elapsed since parameter values were last
     * fetched from the Remote Config server. This method uses the default
     * cache expiration of 12 hours.
     * @return
     */
//    public Task<Void> fetch(){
//        return getObject().fetch();
//    }

    public boolean activateFetched(){
        return getObject().activateFetched();
    }
    /**
     * Gets the current state of the
     * FirebaseRemoteConfig singleton object.
     *
     * returns A FirebaseRemoteConfigInfo wrapping the current state.
     */
    public FirebaseRemoteConfigInfo getInfo(){
        return getObject().getInfo();
    }

    /**
     * Sets defaults in the default namespace,
     * using an XML resource.
     * @param resourceId
     */
    @Hide()
    public void setDefaults (String resource){
         BA.Log("Calling SetDefaults... "+resource);
        int resourceId=BA.applicationContext.getResources().getIdentifier(resource, "values", BA.packageName);
         BA.Log("resourceID="+resourceId);
       
        //getObject().setDefaults(arg0);
        //getObject().setDefaults(arg0, namespace);
        //getObject().setDefaults(Object map);
        //java.util.Map javaMap = map.getObject();
    }
   
    public void fetch(int cacheExpiration){
        getObject().fetch(cacheExpiration)
    .addOnCompleteListener(new OnCompleteListener<Void>() {
      @Override
      public void onComplete(Task<Void> task) {
          if (task.isSuccessful()) {
              BA.Log("Fetch Succeeded");
                     if (ba.subExists(eventName + "_onfetchcomplete")) {
                      BA.Log("lib:Raising.. "+eventName + "_onfetchcomplete()");                               
                      //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                      ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onfetchcomplete", true, new Object[] {task.isSuccessful()});
                  }else {
                      BA.Log("lib: NOTFOUND '"+eventName + "_onfetchcomplete");
                  }
                     // Once the config is successfully fetched it must be activated before newly fetched
                     // values are returned.
            //getObject().activateFetched(); 
          } else {
              BA.LogError("Fetch failed");
                       if (ba.subExists(eventName + "_onfetchcomplete")) {
                        BA.Log("lib:Raising.. "+eventName + "_onfetchcomplete()");                               
                        //app.raiseEvent(app.context, eventName+"_pagerendered", i, pageCount, filename+"-" + i + ".png");
                        ba.raiseEventFromDifferentThread(this, null, 0, eventName + "_onfetchcomplete", true, new Object[] {task.isSuccessful()});
                    }else {
                        BA.Log("lib: NOTFOUND '"+eventName + "_onfetchcomplete");
                    }
          }
      }
  });
    }
   
   
    @SuppressWarnings("unchecked")
    public void Defaults(Map defaults){
        @SuppressWarnings("rawtypes")
        java.util.Map m = defaults.getObject();
        getObject().setDefaults(m);
    }
    public void setDefaults2(Map defaults, String namespace){
        @SuppressWarnings("rawtypes")
        java.util.Map m = defaults.getObject();
        getObject().setDefaults(m, namespace);
    }

//    /**
//     * Sets defaults in the given namespace,
//     * using an XML resource
//     * @param resourceId
//     */
//    @Hide()
//    public void Defaults2(int resourceId, String namespace){
//        getObject().setDefaults(resourceId, namespace);
//    }

    /**
     * Changes the settings for the FirebaseRemoteConfig
     * object's operations, such as turning the developer mode on.
     */
    public void setConfigSettings (FirebaseRemoteConfigSettings settings){
        getObject().setConfigSettings(settings);
    }
    /**
     * Gets the FirebaseRemoteConfigValue
     * corresponding to the specified key.
     */
    public FirebaseRemoteConfigValue getValue(String key){
        return getObject().getValue(key);
    }
    public FirebaseRemoteConfigValue getValue2(String key,String namespace){
        return getObject().getValue(key,namespace);
    }

    /**
     * Gets the value corresponding to the specified
     * key as a byte array.
     *
     * Returns Value as a byte array if a value
     * corresponding to the look up key was present;
     * default (if set) or static default value otherwise.
     */
    public byte[] getByteArray(String key){
        return getObject().getByteArray(key);
    }
    /**
     * Gets the value corresponding to the specified
     * key, in the specified namespace, as a byte array.
     *
     * Returns Value as a byte array if a value
     * corresponding to the look up key was present;
     * default (if set) or static default value otherwise.
     */
    public byte[] getByteArray2(String key,String namespace){
        return getObject().getByteArray(key,namespace);
    }

    /**
     * Gets the value corresponding to
     * the specified key, as a string.
     * 
     * returns alue as a string if a value corresponding
     * to the look up key was present and could be converted
     * to a string; default (if set) or
     * static default value otherwise.
     */
    public String getString(String key){
        return getObject().getString(key);
    }
    /**
     * Gets value as a string corresponding
     * to the specified key in the specified namespace.
     * returns alue as a string if a value corresponding
     * to the look up key was present and could be converted
     * to a string; default (if set) or
     * static default value otherwise.
     */
    public String getString2(String key,String namespace){
        return getObject().getString(key,namespace);
    }
   
   
    /**
     * Gets the value corresponding to the specified
     * key, in the specified namespace, as a double.
     *
     * Returns Value as a double if a value corresponding
     * to the look up key was present and could be
     * converted to a double; default (if set) or
     * static default value otherwise.
     */
    public double getDouble(String key){
        return getObject().getDouble(key);
    }   
    /**
     * Gets the value corresponding to the specified
     * key as a double.
     *
     * Returns Value as a double if a value corresponding
     * to the look up key was present and could be
     * converted to a double; default (if set) or
     * static default value otherwise.
     */
    public double getDouble2(String key,String namespace){
        return getObject().getDouble(key,namespace);
    }
   
   
    /**
     * Gets the value corresponding to the specified key,
     * as a boolean.
     */
    public boolean getBoolean(String key){
        return getObject().getBoolean(key);
    }
    /**
     * Gets the value corresponding to the specified key,
     * as a boolean, in the specified namespace.
     */
    public boolean getBoolean2(String key,String namespace){
        return getObject().getBoolean(key,namespace);
    }
   
    /**
     * Gets the value corresponding to the
     * specified key, as a long.
     * 
     * returns Value as a long if a value corresponding
     * to the look up key was present and could be
     * converted to a long; default (if set) or
     * static default value otherwise. 
     */
    public long getLong(String key){
        return getObject().getLong(key);
    }
    /**
     * Gets the value corresponding to the specified
     * key, in the specified namespace, as a long.
     */
    public long getLong2(String key,String namespace){
        return getObject().getLong(key,namespace);
    }
       
}


and

B4X:
package de.donmanfred;

import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;

import android.content.Context;
import android.widget.TextView;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.ShortName;

@ShortName("RemoteConfigValue")
//@ActivityObject()
//@Permissions(values={"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE"})
//@Events(values={"onSigned(sign As Object)"})
//@DependsOn(values={"android-viewbadger"})
//@DesignerProperties(values = {
//        @Property(key="Checked", displayName="Checked", defaultValue="False", fieldType="Boolean")
//    })
public class FirebaseRemoteConfigValueWrapper extends AbsObjectWrapper<FirebaseRemoteConfigValue> {
    private BA ba;

    public void Initialize(final BA ba, FirebaseRemoteConfigValue cfg) {
        final FirebaseRemoteConfigValue _obj = cfg;
        setObject(_obj);
    }

    public boolean asBoolean (){
        return getObject().asBoolean();
    }
    public byte[] asByteArray(){
        return getObject().asByteArray();       
    }
    public double asDouble(){
        return getObject().asDouble();
    }
    public long asLong(){
        return getObject().asLong();
    }
    public String asString(){
        return getObject().asString();
    }
    /**
     * VALUE_SOURCE_REMOTE if the value was retrieved
     * from the server, VALUE_SOURCE_DEFAULT if the
     * value was set as a default, or VALUE_SOURCE_STATIC
     * if no value was found and a static default
     * value was returned instead.
     */
    public int getSource(){
        return getObject().getSource();
    }
}

Hope it helps
 

DonManfred

Expert
Licensed User
Longtime User
Manfred, in this library you using a jar not aar, am i right?
inside eclipse i used the classes.jar from the aar to be able to reference and to use the imports.
but b4a will use the google aars due to the depends-on directive.
if you want to directly reference a aar you can use

B4X:
@DependsOn(values={"someaarfilename.1.2.3.aar")

but you still need to ectract trhe classes.jar from the aar and add it to eclipses build-paths.
In this case you need to copy the aar to additional libs folder.
 

ktlua

Member
Licensed User
Assuming that you have an aar file:

1. Open the aar file with 7zip and extract classes.jar.
2. Put this jar in the libs folder.
3. In the @DependsOn annotation you should write: @DependsOn(values={"NameOfTheAAR.aar"})

Your library will be made of 3 files:
<your library>.xml
<your library>.jar
<aar file>.aar

The advantage of aar libraries is that assets and resources are added automatically.

I am sorry that I am to this. I have an arr file form PayPal-Android-SDK-master:payPalAndroidSDK-aar.
Kindly help guide me how to include this to my program. Do I need to process the files before it can be used?
 

ktlua

Member
Licensed User
Thank you.

I find a class.jar under the PayPalAndroidSDK-2.15.1.aar.
I will try to use it.
 
Top