How can I use Java Plugin in Unity - android

I try make a plugin in android studio to use in Unity.
So I make this method in android studio.
public class MainActivity extends UnityPlayerActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public static String SendMessage(){
return "Message";
}
}
and in Unity,
public class PluginWrapper : MonoBehaviour {
// Use this for initialization
void Start () {
TextMesh textMesh = GetComponent<TextMesh> ();
var plugin = new AndroidJavaClass ("com.thewell_dev.beaconf.MainActivity");
textMesh.text = plugin.CallStatic<string> ("ReturnMessage");
}
}
to use SendMessage() method in unity, using AndroidJavaClass and CallStatic.
It is success.
I can check message in device by unity.
But, one error occurs.
If I change method like this,
public class MainActivity extends UnityPlayerActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public String SendMessage(){
return "Message";
}
}
change SendMessage method type.(static String to String)
and in Unity,
public class PluginWrapper : MonoBehaviour {
// Use this for initialization
void Start () {
TextMesh textMesh = GetComponent<TextMesh> ();
var plugin = new AndroidJavaClass ("com.thewell_dev.beaconf.MainActivity");
textMesh.text = plugin.Call<string> ("ReturnMessage");
}
}
change plugin.CallStaic to plugin.Call
In case, I just delete Static, but it returns no result.
https://docs.unity3d.com/kr/current/ScriptReference/AndroidJavaClass.html
I just change the type. But no returns.
I think I mistake to Call method by AndroidJavaClass,
but I can't find it.
If you know about it, Please help me.

You can use "types" to invoke static methods, as static things are bound to their defining types. You can't, however, use instance methods (non-static methods, that is) without an instance of the type. That's why your second attempt fails. To invoke instance methods, you must get an instance of the type, either by making one yourself by invoking the java constructor like this:
var object = new AndroidJavaObject ("com.some.class", constructor_param1, constructor_param2, constructor_param3, ...);
result = object.Call<string> ("SomeMethod");
Or, to get as instance of something like an activity which is constructed outside the scope of your C# script, you use static methods to retrieve the instance you're looking for:
var type = new AndroidJavaClass ("com.some.class");
var object = type.CallStatic<AndroidJavaObject>( "SomeStaticGetterMethod" );
result = object.Call<string> ("SomeMethod");

Related

null point object using asset manager

#Override
public void show()
{
main = new MainClass();
texture = main.manager.get("ball.png");
mainActor = new MainActor(world, texture, new Vector2(1,2));
stage.addActor(mainActor);
}
Attempt to invoke virtual method 'java.lang.Object com.badlogic.gdx.assets.AssetManager.get(java.lang.String)' on a null object reference
how to fix it?
"manager" is the AssetManager created on my MainClass
my MainClass
public class MainClass extends Game
{
protected AssetManager manager;
#Override
public void create()
{
manager = new AssetManager();
manager.load("ball.png", Texture.class);
manager.finishLoading();
setScreen(new MainScreen(this));
}
}
You are creating manager object inside create() method, but you are not calling that method. create() method is not constructor - it won't be called automatically. I don't know what Game class constructor does but most likely not it's not calling create(), but that method is called at some other occasion.
I am assuming your first block of code is from within your MainScreen class. It doesn't make sense for your MainScreen to create a new MainClass Game instance. And since it is, you are referencing a MainClass instance that has never had create() called on it, so its manager is null.
Since I can see that your MainScreen class already takes an instance of MainClass, you just need to make sure it's storing it and you don't instantiate a new one. In this sort of case, I declare passed-in variables as final so I know they are not going to change (and it's impossible to change them accidentally).
public class MainScreen implements Screen {
final MainClass main;
public MainScreen(MainClass main){
this.main = main;
}
//...
#Override
public void show()
{
texture = main.manager.get("ball.png");
mainActor = new MainActor(world, texture, new Vector2(1,2));
stage.addActor(mainActor);
}
//...
}

android embedding unity: ways to initialize and call a SQLite java DB object from unity, which needs "Context"?

I have a SQLite helper class like:
public class GameDbHelper extends SQLiteOpenHelper {...}
If in the android java world, I have to initialize it in the body of an activity like:
public class MainActivity extends AppCompatActivity {
...
GameDbHelper gameDBHelper;
protected void onCreate(Bundle savedInstanceState) {
...
gameDBHelper = new GameDbHelper(this);// "this" being the Context
// database: what ultimately is needed to perform CRUD
database = databaseHelper.getWritableDatabase();
}
I want to initialize this gameDBHelper in Unity side and from there call, say, database.insert(...);
From Unity documentation I learned it is possible to initialize an object to call it's methods:
void Start() {
AndroidJavaObject jo = new AndroidJavaObject("android.content.res.Configuration");
jo.Call("setToDefaults");
}
I think that perhaps I must first initialize the "gameDBHelper = new GameDbHelper(this);" in the Unity Activity:
- export the Unity project into Android Studio
- in the UnityPlayerActivity, define object and initialize it
- "somehow" call it from Unity code (defined before exporting)
A different approach:
- to export the database class as a lib from Android studio into Unity
it is more comfortable for me in Android studio, because in this project I have, Unity is not to be the launcher activity.
Please instruct me how to properly do this.
I try to document relevant parts of my own solution, just in case that may help someone. Please if you see any problem in it, let me know (I'm only learning).
I didn't know that I could pass (inject) "Context" to the constractor of a java class that need it, from Unity side. I found it on this forum, thaks to the forum!
Unity side:
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
AndroidJavaObject jo = new AndroidJavaObject("com.my.package.MyDbHelper", context);
string word = jo.Call<string>("returnData");
tx.text = word;
This will pass context to an Android studio java class constructor, which will then initialize a SQLite database and a helper class that will manipulate it. The class relevant data as example, in Android studio:
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
// implement as documentation describes, link is provided bellow
}
// this is the class, addressed from Unity
public class MyDbHelper{
String str = null;
private SQLiteDatabase db;
MySQLiteOpenHelper mySQLiteOpenHelper;
public myDbHelper(Context context) {
mySQLiteOpenHelper = new mySQLiteOpenHelper(context);
db = mySQLiteOpenHelper.getWritableDatabase();
}
// this is the method that unity calls
public String returnData(){
return queryAndDisplayAll();
}
private String queryAndDisplayAll() {
// using Cursor and db perform query and get the result, say, in a str, and return it, that is like:
// Cursor cursor = db.query(DicSQLiteOpenHelper.TABLE_NAME,...
// str = ...result from Curson...
return (str == null ? "No result!" : str);
}
Here you could read about the SQLiteOpenHelper:
Link to SQLiteOpenHelper documentation.

Mock activity class member before onCreate

I am trying to test an activity using robolectric 3.3.2.
Want to mock and activity's member initialization as the direct call results in NPE.
ActivityController<MyActivity> activityController =
Robolectric.buildActivity(MyActivity.class);
mTestActivity = activityController.get();
Mockito.when(mTestActivity.getCountry()).thenReturn("xxxx");
activityController.setup();
Tried out above code, but the setup.() (oncreate) ignores the mock of
getCountry method and invokes the definition from activity.
Is there a way to achieve this?
It will not work like this even if you use spies (#Spy, Mockito.spy()).
You should use stub:
public class MyActivityTest{
public static class StubMyActivity extends MyActivity {
Country getCountry() {
return someSpecialCountry;
}
}
#Before
public void setUp(){
ActivityController<StubMyActivity> activityController =
Robolectric.buildActivity(StubMyActivity.class);
mTestActivity = activityController.setup().get();
}
}

Mockito - right way to test a method with params

I need your assistance please:
I have this method:
public class MyTestClass {
protected void foo(JSONObject result, String text){
String userId = result.optString("name");
mApi.sendInfo(userId, text, mListener);
}
}
In Mockito I do:
#Test
public void whenFooIsCalledThenSendInfoGetsCalled(){
MyTestClass testClassSpy = spy(mMyTestClass);
JSONObject jsonOb = mock(JSONObject.class);
when(jsonOb.optString("name")).thenReturn("something");
testClassSpy.foo(eq(jsonOb), anyString());
....
some verification....
The problem is, the when the foo method gets called, JSONObject result is null.
I can't seem to get this to work. I thought that if I mock the object and make it return a String once optString("name") is called, would solve this issue but it seems NPE is all I get.
What am I doing wrong?
Thanks you
I am not from Java world but when I look on this code snippet I'm not sure what you want to test. If you want to verify exactly what your test method suggest whenFooIsCalledThenSendInfoGetsCalled then:
You should create spy for mApi
You should create stub for JSONObject result
You should use real implementation of MyTestClass
So your SUT class should allow for injecting dependencies:
public class MyTestClass {
private mApi;
public MyTestClass(Api api) {
mApi = api;
}
void foo(JSONObject result, String text){ /* your implementation */}
}
And your test method:
#Test
public void whenFooIsCalledThenSendInfoGetsCalled(){
// arrange test
Api spyApi = spy(Api.class);
JSONObjec stub = mock(JSONObject.class);
when(stub.optString("name")).thenReturn("something");
MyTestClass sut = new MyTestClass(spyApi);
// Act
sut.foo(stub, "text");
// Assert
verify(spyApi , times(1)).foo(eq("something"), "text", listener);
}

How do I write an android JUnit test when my activity relies on extras passed through an Intent?

I am writing an android Junit test for a class that relies on extras passed to it through an Intent. I was able to get the class working properly, but I would still like to know how to write a unit test for such a class, as the test still fails.
public class AddClassEvent extends Activity{
private String eventType;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
final String cNo = extras.getString("CourseNum");
// create a model instance
final StudentDBModel model = new StudentDBModel(this);
setContentView(R.layout.add_class_event);
.....
.....
}
}
The test class looks like...
public class AddClassEventTest extends ActivityInstrumentationTestCase2<AddClassEvent>{
private StudentDBModel model = null;
private RenamingDelegatingContext context = null;
public AddClassEventTest() {
super("com.UI", AddClassEvent.class);
}
/**
* This method is called before each test.
*/
#Override
public void setUp() {
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
}
/*
* This function will test addNewClassEvent() from StudentDBModel
*/
public void testAddNewClassEvent(){
ContentValues courseValues = new ContentValues();
courseValues.put("CourseId", "60-415");
courseValues.put("CourseName", "Advanced Database Design");
courseValues.put("Section", "1");
courseValues.put("Location", "Erie");
courseValues.put("Credit", "3");
courseValues.put("ProfEmail", "rfortier#uwindsor.ca");
courseValues.put("Website", "cs.uwindsor.ca");
model.addNewCourses(courseValues);
int numEventsBefore = model.getNumClassEvents();
ContentValues values = new ContentValues();
values.put("EventName", "Assignment 1");
values.put("CourseId", "60-415");
values.put("EventType", "Assignment");
values.put("EventWeight", "8");
values.put("DueDate", "10/20/2010");
model.addNewClassEvent(values);
int numEventsAfter = model.getNumClassEvents();
assertEquals(numEventsBefore + 1, numEventsAfter);
}
}
The problem is, the extra that I am passing to the class AddClassEvent is a PK for my DB that is created in another class and passed to AddClassEvent through an Intent. Whenever I run the test I get a NULL Pointer Exception on the on the line:
final String cNo = extras.getString("CourseNum");
How do I create the info from the extra in the Junit Test? Is there a way to get this test to work? I have searched extensively and can't find an answer. Is there some way to falsely create the extras in the Junit test so that it thinks it is being created by the other class? If so, could someone please show me how?
OK so I have tried to take your advice and I have changed my setUp function to:
#Override
public void setUp() {
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
Intent addEvent = new Intent();
addEvent.setClassName("com.UI", "com.UI.AddClassEvent");
addEvent.putExtra("CourseNum", "60-415");
setActivityIntent(addEvent);
getActivity();
}
but I am still getting a NULL Pointer exception. Is my syntax wrong? Any suggestions?
The class you inherit, ActivityInstrumentationTestCase2, allows you to mock Intents. From the documentation:
You can inject custom Intents into your Activity (see setActivityIntent(Intent)).
The documentation for setActivityIntent() further clarifies:
Call this method before the first call
to getActivity() to inject a
customized Intent into the Activity
under test.
If you do not call this, the default
intent will be provided. If you call
this after your Activity has been
started, it will have no effect.
So you should be able to place a call to this method inside your setUp() before your call to getActivity(). You can pass in a mocked Intent into setActivityIntent like you mentioned -- just build a fake Intent with extras that you'd expect the Activity to see.
OK, I figured out my mistake! The code for setUp was just in the wrong order. It should look like:
#Override
public void setUp() {
Intent addEvent = new Intent();
addEvent.setClassName("com.UI", "com.UI.AddClassEvent");
addEvent.putExtra("CourseNum", "60-415");
setActivityIntent(addEvent);
context = new RenamingDelegatingContext(getActivity(), "test_");
model = new StudentDBModel(context);
}
I was calling getActivity() twice and the first call was ahead of the Intent. By using the correct order, the test runs fine. Thanks for the help McStretch.

Categories

Resources