Android Tutorial Accessing third party Jar with #Additionaljar and JavaObject - Picasso

The #AdditionalJar module attribute (introduced in B4A v3.80) allows us to reference external jars.
With the help of JavaObject it is now possible to integrate third party jars without a wrapper.

This solution is good for "simple" libraries. If the API is complicated with many interfaces then it will be easier to create a wrapper.

As an example we will use Picasso image downloader to download images: http://square.github.io/picasso/

upload_2014-5-12_13-9-8.png


The first step is to download the third party jar and put it in the additional libraries folder.
We then use #AdditionalJar to tell the compiler to add a reference to this jar:
B4X:
#AdditionalJar: picasso-2.2.0
Note that the jar extension is omitted. You can call #AdditionalJar multiple times if multiple jars are required.

The following two subs will usually be required. They allow you to get the "context" (it will be an android.app.Activity when called from an Activity module).

This code should be added to an activity or service directly.
B4X:
Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

SS-2014-05-12_13.15.19.png


As you can see in their examples we always start by calling Picasso static method 'with'. It is more clear in the JavaDocs page: http://square.github.io/picasso/javadoc/com/squareup/picasso/Picasso.html

This sub will call the static method:
B4X:
Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Now we will implement the above Java code:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))

In the second example we call a more complex API:
B4X:
'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
   .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))

The third example is more interesting. We download an image with a callback event that is raised when download completes.

The first step it to create the interface. This is done with JavaObject.CreateEvent (or CreateEventFromUI).
In this case we are implementing com.squareup.picasso.Callback: http://square.github.io/picasso/javadoc/com/squareup/picasso/Callback.html
B4X:
Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
The last parameter is the default return value. This value will be used if the event cannot be raised (activity is paused for example). In this case we return Null.
The event sub:
B4X:
Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub
MethodName - The interface method name (onSuccess or onError in this case).
Args - An array of parameters passed to this method. In this case there are no parameters.
All this information is from Picasso JavaDocs: http://square.github.io/picasso/javadoc/index.html?com/squareup/picasso/Callback.html

The last step is to call the method that expects the callback:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))

As this library requires the INTERNET permission we need to manually add it to the manifest editor:
B4X:
AddPermission(android.permission.INTERNET)

The complete code:
B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
#End Region

#AdditionalJar: picasso-2.2.0

Sub Process_Globals

End Sub

Sub Globals
   Dim img1, img2 As ImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   img1.Initialize("")
   Activity.AddView(img1, 0, 0, 100%x, 50%y)
   img2.Initialize("")
   Activity.AddView(img2, 0, 50%y, 100%x, 50%y)
   Dim url As String = "http://i.imgur.com/DvpvklR.png"
   'first example: Picasso.with(context).load(url).into(imageView);
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))
  
   'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
     .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))
  
   'third example: download image with callback
   Dim jo As JavaObject = GetPicasso
   Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))
End Sub

Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub

Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
 

susu

Well-Known Member
Licensed User
Longtime User
I made a simple app with 2 activities:
1. Main activity with a button to start LoadImage activity.
2. LoadImage activity has a scrollview with random 20 images (download from internet by using Picasso).

It works fine but there's only one problem: From Main activity I click on button to start LoadImage activity, if I back to Main activity but the images still loading so it will auto open LoadImage activity again. How can I cancel the loading of images when LoadImage activity closed? I try to remove all ImageView and ScrollView in Sub Activity_Pause but it seems not work.

Thanks.
 

PhilipK

Member
Licensed User
Longtime User
Hi, I'm trying to integrate a third party JAR file: thermodosdk-1.0.18

I can't see from the examples above exactly how to do it; when I'm sure it's straightforward if you know what's to be done.

The author says you have to :
1. Declare the proper permissions required by the SDK. (OK done).
2. Implement a ThermodoListener that will be notified of Thermodo related events
3. Create a Thermodoinstance and start measuring
(From the authors site: https://github.com/thermodo/ThermodoSDK-Android/blob/master/README.md)
  1. Where do I put the JAR file? Into the B4A libraries folder?
  2. I'm confused on how to reference the JAR beyond the obvious: #AdditionalJar: thermodosdk-1.0.18
  3. I have the permissions from their site to add to the manifest

I have found code that has the package name and also lists the events:

B4X:
package com.robocatapps.thermodosdk.sample;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.robocatapps.thermodosdk.Thermodo;
import com.robocatapps.thermodosdk.ThermodoFactory;
import com.robocatapps.thermodosdk.ThermodoListener;

import java.util.logging.Logger;

public class MainActivity extends Activity implements ThermodoListener {

    private static Logger sLog = Logger.getLogger(MainActivity.class.getName());
    private Thermodo mThermodo;
    private TextView mTemperatureTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTemperatureTextView = (TextView) findViewById(R.id.temperatureTextView);
        mThermodo = ThermodoFactory.getThermodoInstance(this);
        mThermodo.setThermodoListener(this);
    }


    @Override
    public void onStartedMeasuring() {
        Toast.makeText(this, "Started measuring", Toast.LENGTH_SHORT).show();
        sLog.info("Started measuring");
    }

    @Override
    public void onStoppedMeasuring() {
        Toast.makeText(this, "Stopped measuring", Toast.LENGTH_SHORT).show();
        mTemperatureTextView.setText(getString(R.string.thermodo_unplugged));
        sLog.info("Stopped measuring");
    }

    @Override
    public void onTemperatureMeasured(float temperature) {
        mTemperatureTextView.setText(Float.toString(temperature));
        sLog.fine("Got temparature: " + temperature);
    }

    @Override
    public void onErrorOccurred(int what) {
        Toast.makeText(this, "An error has occurred: " + what, Toast.LENGTH_SHORT).show();
        switch (what) {
            case Thermodo.ERROR_AUDIO_FOCUS_GAIN_FAILED:
                sLog.severe("An error has occurred: Audio Focus Gain Failed");
                mTemperatureTextView.setText(getString(R.string.thermodo_unplugged));
                break;
            case Thermodo.ERROR_AUDIO_RECORD_FAILURE:
                sLog.severe("An error has occurred: Audio Record Failure");
                break;
            case Thermodo.ERROR_SET_MAX_VOLUME_FAILED:
                sLog.warning("An error has occurred: The volume could not be set to maximum");
                break;
            default:
                sLog.severe("An unidentified error has occurred: " + what);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mThermodo.start();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mThermodo.stop();

How does it fit together? I can take the embarrassment,... I hope.

Thanks in advance
 

Jordi Casas Armengol

Member
Licensed User
Longtime User
After read this post, I wonder does anybody try Glide with JavaObject?
http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

I'm trying to use Glide library but I can't load an image into a Bitmap, only on an ImageView. This works:

B4X:
myImageView(i).Initialize("")
GetGlide.RunMethodJO("load", Array(sPIC)).RunMethodJO("asBitmap",Array()).RunMethodJO("into", Array(myImageView(i)))

The ImageView is shown correctly if I assign it to activity background, but if I try to get the associated bitmap (myImageView(i).Bitmap) it returns null.

Anybody has a solution?
 

tagwato

Member
Licensed User
Longtime User
Anyone succeeded in running the exact code in post # 1 (complete code) ?
Tried (B4A 5.80) in emulator and real device, no success. Images are not loaded and callback method name always gets "on error".
The url is Ok, we can see the image in the browser.
Any tip?
 

ppgirl

Member
Licensed User
Longtime User
Erel , How to deal with @override instead of createEvent ?

I found a library need callback by @override function from the classes , I did not find other interface .

please suggest the javaobject usage . thanks!


private void getYoutubeDownloadUrl(String youtubeLink) {
YouTubeUriExtractor ytEx = new YouTubeUriExtractor(this) {
@override
public void onUrisAvailable(String videoId, String videoTitle, SparseArray<YtFile> ytFiles) {
mainProgressBar.setVisibility(View.GONE);
if (ytFiles == null) {
// Something went wrong we got no urls. Always check this.
finish();
return;
}
// Iterate over itags
for (int i = 0, itag = 0; i < ytFiles.size(); i++) {
itag = ytFiles.keyAt(i);
// ytFile represents one file with its url and meta data
YtFile ytFile = ytFiles.get(itag);
// Just add videos in a decent format => height -1 = audio
if (ytFile.getMeta().getHeight() == -1 || ytFile.getMeta().getHeight() >= 360) {
addButtonToMainLayout(videoTitle, ytFile);
}
}
}
};
// Ignore the webm container format
ytEx.setIncludeWebM(false);
ytEx.setParseDashManifest(true);
// Lets execute the request
ytEx.execute(youtubeLink);
}
 

scsjc

Well-Known Member
Licensed User
Longtime User
Hello,
it's possible get the ... ImageView.bitmap ... on event Callback_Event,
and process the bitmap to have RoundBitmap with NativeMe.Runmethod("getRoundBitmap"......

(the idea is make a roundbitmap imageview obtain by picasso jar)
thanks
 

deyvitm

Member
Licensed User
Longtime User
The #AdditionalJar module attribute (introduced in B4A v3.80) allows us to reference external jars.
With the help of JavaObject it is now possible to integrate third party jars without a wrapper.

This solution is good for "simple" libraries. If the API is complicated with many interfaces then it will be easier to create a wrapper.

As an example we will use Picasso image downloader to download images: http://square.github.io/picasso/

View attachment 24962

The first step is to download the third party jar and put it in the additional libraries folder.
We then use #AdditionalJar to tell the compiler to add a reference to this jar:
B4X:
#AdditionalJar: picasso-2.2.0
Note that the jar extension is omitted. You can call #AdditionalJar multiple times if multiple jars are required.

The following two subs will usually be required. They allow you to get the "context" (it will be an android.app.Activity when called from an Activity module).

This code should be added to an activity or service directly.
B4X:
Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

SS-2014-05-12_13.15.19.png


As you can see in their examples we always start by calling Picasso static method 'with'. It is more clear in the JavaDocs page: http://square.github.io/picasso/javadoc/com/squareup/picasso/Picasso.html

This sub will call the static method:
B4X:
Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Now we will implement the above Java code:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))

In the second example we call a more complex API:
B4X:
'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
   .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))

The third example is more interesting. We download an image with a callback event that is raised when download completes.

The first step it to create the interface. This is done with JavaObject.CreateEvent (or CreateEventFromUI).
In this case we are implementing com.squareup.picasso.Callback: http://square.github.io/picasso/javadoc/com/squareup/picasso/Callback.html
B4X:
Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
The last parameter is the default return value. This value will be used if the event cannot be raised (activity is paused for example). In this case we return Null.
The event sub:
B4X:
Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub
MethodName - The interface method name (onSuccess or onError in this case).
Args - An array of parameters passed to this method. In this case there are no parameters.
All this information is from Picasso JavaDocs: http://square.github.io/picasso/javadoc/index.html?com/squareup/picasso/Callback.html

The last step is to call the method that expects the callback:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))

As this library requires the INTERNET permission we need to manually add it to the manifest editor:
B4X:
AddPermission(android.permission.INTERNET)

The complete code:
B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
#End Region

#AdditionalJar: picasso-2.2.0

Sub Process_Globals

End Sub

Sub Globals
   Dim img1, img2 As ImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   img1.Initialize("")
   Activity.AddView(img1, 0, 0, 100%x, 50%y)
   img2.Initialize("")
   Activity.AddView(img2, 0, 50%y, 100%x, 50%y)
   Dim url As String = "http://i.imgur.com/DvpvklR.png"
   'first example: Picasso.with(context).load(url).into(imageView);
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))
 
   'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
     .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))
 
   'third example: download image with callback
   Dim jo As JavaObject = GetPicasso
   Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))
End Sub

Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub

Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

I have put the picasso library inside InfoWindowAdapter1_GetInfoContents in google maps, when I click to show InfoWindows the image does not appear, but when I next give another image to InfoWindows if it appears, any ideas?

Source:

GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(ImageView1))
ImageView1.Gravity = Gravity.FILL
 
Top