Retrieve android:versionName from Library Project - android

I have created a Library Project which I import into another project.
In that Library Project at some point I retrieve it's android:versionName
To do that you need to supply the package name.
The problem arises when that code is executed when the Library Project is included within another project, then it seems that that code throws an exception :
10-04 10:15:36.987: WARN/System.err(1407): getSoftwareVersion(), Caught Exception : android.content.pm.PackageManager$NameNotFoundException: mobilaria.android.LandenPlayerCodeBase.baseplayer
Thats the package name of the package of the Project Library... it seems it cannot find it even though the same code that is executing that call is part of the Library itself...
Does anyone have experienced something like this or has an idea on how to solve this ?

As far as I know android library project manifest is ignored at the moment, manifest is not merged into end application when you reference a library. Hence you cant extract any data from the library's manifest.

I just tried something similar.
I tried to add a method getLibraryVersion() to my custom Application class. So I would be able to call
MyLibrary.getLibraryVersion()
from within the code that included that library. But it seems that you can not access the String resources via getText() or getString() like this:
public class MyLibrary extends Application {
#Override
public void onCreate() {
super.onCreate();
// provide an instance for our static accessors
MyLibrary.instance = this;
}
private static void checkInstance() {
if (MyLibrary.instance == null) {
throw new IllegalStateException("Application not created yet!");
}
}
/**
* #return the library name
*/
public String getLibraryName() {
MyLibrary.checkInstance();
return MyLibrary.instance.getString(R.string.app_project_name).toString();
}
...
}
Because the onCreate() method seems not to be called, the instance is always null!
As this way was not working out, and as you saw you cannot access the version the way you tried, I just hard coded the version and the library name, into my custom application class like this:
public class MyLibrary extends Application {
/**
* #return the library name
*/
public String getLibraryName() {
return "org.yourCompany.android.lib.YourLibName";
}
/**
* #return the library version
*/
public String getLibraryVersion() {
return "1.0.0";
}
}
I know that this is kind of a dirty solution, and I would prefer a cleaner version of coding, with these Strings stored as String resources in strings.xml but I don't know any better way. So you just have to change the library name and version in your manifest or better the strings.xml AND in the Application class.
But how often do you change the library name or version?
Hope this can help somebody and save time!
PS: some of the above code is based on this:
http://blog.tomgibara.com/post/126377651/global-application-state-in-android

Related

How to configure Android library from an app that imports it?

I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants

How can I mock and test this class?

The follows was the code which I want to test.
public class Demo {
private static final List<Pair<String, String>> mList;
static {
mList = new ArrayList<>();
mList.add(new Pair<>("F0", "T1"));
mList.add(new Pair<>("F1", "T2"));
mList.add(new Pair<>("F2", "T3"));
}
public String getStr(int pos) {
return mList.get(pos).first;
}
}
I was an android developer. I have get some trouble in test and mock the code.I have use mockito.
I have try some code to test it,but the result was not my expect.
1.First try
#Test
public void test(){
Demo demo=new Demo();
assertEquals(demo.getStr(0),"F0");
/**
* java.lang.AssertionError:
* Expected :null
* Actual :F0
*/
}
2.Second try
#Test
public void test() {
Demo demo = mock(Demo.class);
doCallRealMethod().when(demo).getStr(0);
assertEquals(demo.getStr(0), "F0");
/**
* java.lang.AssertionError:
* Expected :null
* Actual :F0
*/
}
Anyone tell me how can I resolve this problem to make demo.getStr(0) == "F0" by call the real method? Thanks!
===========================
Another question relate to it
I have try an another test to test android.util.Pair class, and the result is that "pair.first" was null,.(There are androidTest and test directory,I put it into test package.Did it impact the result?)
import android.util.Pair;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
public class DemoTest {
#Test
public void test1(){
Pair<String,String> pair=new Pair("First","Second");
assertEquals("First",pair.first);
//pair.first was null,why?
}
#Test
public void test2(){
Pair<String,String> pair= Mockito.spy(Pair.class);
assertEquals("First",pair.first);
//pair.first was null also,why?
}
}
Why the simple code is correct in real android environment,but failure in test?
I had the same problem too. month ago I have problem with TextUtils class too.
I report this to jUnit but they told me the problem is with android package because in unit test environment you don't have access to platform specific classes
for that pair case you can use this package. this works for me
import android.support.v4.util.Pair;
The problem in your first try is, that the public field "first" is actually null.
Is the Pair class the one from the "javafx.util" package or a custom implementation?
Did you forget "this.first = first" or something similar in the constructor of the "Pair" class?
I would also recommend to change the following line:
assertEquals(demo.getStr(0),"F0");
to
assertEquals("F0", demo.getStr(0));
so that the error is printed correctly.
Your second try does not make any sense. What is the point in mocking the class you want to test?
I think the second example has the same problem as the first one. Pair.first is never set. If you fix that, it should also work (untested).
From Google's Android tools website:
"Method ... not mocked."
The android.jar file that is used to run unit tests does not contain any actual code - that is provided by the Android system image on real devices. Instead, all methods throw exceptions (by default). This is to make sure your unit tests only test your code and do not depend on any particular behaviour of the Android platform (that you have not explicitly mocked e.g. using Mockito).
So how can we solve this?
In other words. If you need a default android class to work properly you either have to include it from a separate repository, or implement it yourself.
In the case of Android's Pair class. You can use android.support.v4.util.Pair instead.
To get access to this class, you can include com.android.support:support-compat:27.0.0 in your build.gradle or dependencies file.
If you are not using Gradle, you can copy the implementation of this file and use it in place of the official one. Or you can try and download the .jar file from this older version https://mvnrepository.com/artifact/com.google.android/support-v4/r7 (I have not tested whether it works)
Another approach (based on this) is to create the class in app/src/test/java/android/util/Pair.java and copy the code from the Android implementation.
This way you don't need extra dependencies. (There may be issues related to the implementation changing after you make the copy, but the dependencies may become stale as well.)

How to invoke getApplicationContext() from jni? [duplicate]

I would like to get the context of application which has reference/hosted my library at run-time inside one class of my library project. Is it possible? If yes, how?
Thanks
Update
I don't want my user to pass context in parameter to my library project because it is possible that my library project will be called through JNI and I have no idea how I can get context in JNI and pass it to Java layer.
There is one more way, add application class in your library project:
/**
* Base class for those who need to maintain global application state.
*/
public class LibApp extends Application {
/** Instance of the current application. */
private static LibApp instance;
/**
* Constructor.
*/
public LibApp() {
instance = this;
}
/**
* Gets the application context.
*
* #return the application context
*/
public static Context getContext() {
return instance;
}
}
Then in your regular project make the real application class extend LibApp:
/**
* Base class for those who need to maintain global application state.
*/
public class App extends LibApp {
#Override
public void onCreate() {
super.onCreate();
}
}
Make sure that you have "Name" defined in AndroidManifest:
<application android:name="App" ...>
and that your App class is in the base package.
You can then use LibApp.getContext() your library project to get the application context of the application that is using the library.
This may not be good solution but it works for me.
I am sharing it because it might be useful to somebody else.
Is it possible?
Yes.
If yes, how?
Pass it in as a parameter.
I don't want my user to pass context in parameter to my library project because it is possible that my library project will be called through JNI and I have no idea how I can get context in JNI and pass it to Java layer.
Then figure out "how [you] can get context in JNI and pass it to Java layer". I would imagine that you would pass it like any other object. As #Blundell noted, you do not really have any other option.
There's another way to get context in jni, neither passing a parameter nor saving context by yourself, but by using android api.
I found that there's a class named:
android.app.AppGlobals
in the source code. And the static function
getInitialApplication
can return an Application object. But it must be called in main thread, and the class is hidden.
Anyway you can use it by reflecting in java. And you can just use FindClass() and FindStaticObjectMethod() to find out the method, and use it.
Hope that helps.
According to this post you can let the library auto-initialize itself with the application context by the aid of a ContentProvider.
Be careful anyway, as described in the post comments, there may be drawbacks concerning loading time and instant run, as well as crashes on multi-process apps.
HTH
I would pass it as a parameter or pass it a singleton in that library.
Having the main app application extend the library's application class is a bad idea coz in java you can only extend once from a class. If your application requires to pass to another library you will be in trouble.

Use own imports for imported project in Java

after many hours of work, I got jaxb working with android.
I created many own jar-files with own prefixes (instead of javax.xml.bind.* I called it rebuild.javax.xml.bind.*).
It works fine for classes in my project, but I have to make it work for classes in an other imported (Java BuildPath ->Projects ) project, but every class of the imported project references to javax.xml. and so it crashes my app if I run it(because in android there is no javax.xml.bind).
I have no write-access to the other project, how can I make it work?
If I copy every class in the second project and chage the imports von javax to rebuild.javax, it works fine, but I can't reimport it every time something changed.
Sry for my bad english, I hope you guys understand me :)
Maybe you could use a custom ClassLoader that replaces occurences of javax.xml.bind.* by rebuild.javax.xml.bind.* just before loading its classes.
Something like this :
public class JaxbClassLoader extends ClassLoader {
[...]
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.startsWith("javax.xml.bind"))
name = "rebuild." + name;
super.loadClass(name);
}
}
Maybe Class.findClass should be overridden too, but i'm not sure.
Then, you create a custom Application class and replace its ClassLoader like this :
private ClassLoader loader;
#Override
public void onCreate() {
Thread.currentThread().setContextClassLoader(getClassLoader());
}
#Override
public ClassLoader getClassLoader() {
if(loader == null)
loader = new JaxbClassLoader(super.getClassLoader());
return loader;
}
Don't forget to declare your Application in the AndroidManifest.
Setting the context class loader is additional, but may be of use.
Overriding getClassLoader() will cause the returned loader to be used in Activities created inside this app context.
Under some specific conditions, you might have to explicitly set the CCL once again, for example, upon creation of new Thread. A ThreadLocal might be of help in this case.

Is it possible to get application's context in an Android Library Project?

I would like to get the context of application which has reference/hosted my library at run-time inside one class of my library project. Is it possible? If yes, how?
Thanks
Update
I don't want my user to pass context in parameter to my library project because it is possible that my library project will be called through JNI and I have no idea how I can get context in JNI and pass it to Java layer.
There is one more way, add application class in your library project:
/**
* Base class for those who need to maintain global application state.
*/
public class LibApp extends Application {
/** Instance of the current application. */
private static LibApp instance;
/**
* Constructor.
*/
public LibApp() {
instance = this;
}
/**
* Gets the application context.
*
* #return the application context
*/
public static Context getContext() {
return instance;
}
}
Then in your regular project make the real application class extend LibApp:
/**
* Base class for those who need to maintain global application state.
*/
public class App extends LibApp {
#Override
public void onCreate() {
super.onCreate();
}
}
Make sure that you have "Name" defined in AndroidManifest:
<application android:name="App" ...>
and that your App class is in the base package.
You can then use LibApp.getContext() your library project to get the application context of the application that is using the library.
This may not be good solution but it works for me.
I am sharing it because it might be useful to somebody else.
Is it possible?
Yes.
If yes, how?
Pass it in as a parameter.
I don't want my user to pass context in parameter to my library project because it is possible that my library project will be called through JNI and I have no idea how I can get context in JNI and pass it to Java layer.
Then figure out "how [you] can get context in JNI and pass it to Java layer". I would imagine that you would pass it like any other object. As #Blundell noted, you do not really have any other option.
There's another way to get context in jni, neither passing a parameter nor saving context by yourself, but by using android api.
I found that there's a class named:
android.app.AppGlobals
in the source code. And the static function
getInitialApplication
can return an Application object. But it must be called in main thread, and the class is hidden.
Anyway you can use it by reflecting in java. And you can just use FindClass() and FindStaticObjectMethod() to find out the method, and use it.
Hope that helps.
According to this post you can let the library auto-initialize itself with the application context by the aid of a ContentProvider.
Be careful anyway, as described in the post comments, there may be drawbacks concerning loading time and instant run, as well as crashes on multi-process apps.
HTH
I would pass it as a parameter or pass it a singleton in that library.
Having the main app application extend the library's application class is a bad idea coz in java you can only extend once from a class. If your application requires to pass to another library you will be in trouble.

Categories

Resources