Android Library Project - android

How can I use an Android Library Project, and overwrite certain classes in the "final project".
In other words, I want to reference a library project, and at the same time, change some of the code..

Probably not quite what Android Library projects are all about but you could achieve that by using the interface and class inheritance features of the java language itself.
Library project seem to deal well with merging/overriding resources from the parent in the child and letting you use one codebase with varying app package names.
Given your updated comment about using library projects to have one edition of the app that uses AdMob and one that doesnt, I've revised this answer...
Assuming you dont mind packaging the AdMob library (~138KB) in with your full/paid edition, the simplest thing to do would be to extend an Applcation class and therein declare an enum and a static method to decide whether AdMob ads should be shown.
public class Application extends android.app.Application
{
public enum EditionType { LITE, FULL};
public static int getAdVisibilityForEdition(Context ctx)
{
if (EditionType.valueOf(ctx.getString(R.string.edition)) == EditionType.FULL)
{
return View.GONE;
}
return View.VISIBLE;
}
}
You'll add this class to the library project only. In all three of the projects you'll need to add an entry to /res/Strings.xml as such:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="edition">LITE</string>
</resources>
It doesnt matter what the value of this string is in the library project, but you'll vary it in the paid vs. ad supported editions using FULL and LITE respectively.
Finally, to make your activities responsive to these values call something like this in your onCreate() or onResume():
View ad = (View)this.findViewById(R.id.your_adMob_view_id)
ad.setVisibility(Application.getAdVisibilityForEdition
(this.getApplicationContext()));
This will work fine with AdMob as the AdMob code wont actually try to get an Ad if the view is not visible.
If you really dont want to have the extra 138K for the AdMob jar and the manifest permissions needed for it in the full edition, there are more elegant ways to do this but would involve using some kind of wrapper around the AdMob stuff and either varying the xml layouts between the editions (to include the AdMob view or not) or inject the AdMob view into the layout programatically.
Hope that helps.

Related

Multiple projects depending on 1 library

I'm not sure SO is the right place to ask this question so let me know if I should maybe post it on ProgrammersSE.
I've got an Android library project which comes with some functionality and some basic XML files. In the nearest future I'll be developing multiple apps which will heavily depend on that library - it's possible that some of them will only differ in that they'll be using different XML layout files and image resources. As far as I know Android will automatically pick the ones from the regular projects instead of the library one if the names of the appropriate files are the same so this shouldn't be a problem.
The problem is I expect that some of the projects will have to have a slightly extended functionality - meaning I'd have to, e.g., extend the classes which are in the library project.
I just tried that out but obviously that didn't work as I wasn't overriding the entire code of a class - just adding to it, meaning I seemingly can't have the the library Activities call the classes from my regular project.
Is there any way around that without using reflection?
Is there maybe a better way of handling such a situation?
Edit for clarification:
Thanks to #jucas and #Alex Cohn for the answers and the links. I'm not sure if the solutions you wrote are applicable to my situation - I'd probably have to see examples of those coded to decide if I can do anything similar in my project.
Here's an example of what makes this problematic for me: say in my library project I've got a class called MyActivity which extends Activity and implements OnScrollChangedListener because there's a ScrollView in it whose background has to scale. There could be something like this in it:
#Override
public void onScrollChanged() {
int currentScrollOffsetY = this.scrollView.getScrollY();
// No case for further back than the bottom of the screen (lower than 0)
// and if it's higher than where it should stop, keep it at that point
if (currentScrollOffsetY > this.screenHeightPx * MULTIPLIER_Y_ANIMATION_STOP) {
currentScrollOffsetY = (int) (this.screenHeightPx * MULTIPLIER_Y_ANIMATION_STOP);
}
// Set the pivot points of the background images
this.imageBackground.setPivotX(this.imageBackground.getWidth() / 2.0f);
this.imageBackground.setPivotY(0);
// Scale the background
float newBackgroundScale = 1 - (float) currentScrollOffsetY / (float) this.screenHeightPx;
if (newBackgroundScale < 0.75f) {
newBackgroundScale = 0.75f;
}
this.imageBackground.setScaleX(newBackgroundScale);
this.imageBackground.setScaleY(newBackgroundScale);
}
As you can see, the new scale for the background image is never smaller than 0.75 of the original size. Now if one of the projects using the library project needed that to be 0.8 instead, I could just move the value from the code to the XML values resources and it should be dynamically read from there - that's perfectly fine.
But what if I not only wanted to do that but also scale another ImageView?
this.imageBackground.setScaleX(newBackgroundScale);
this.imageBackground.setScaleY(newBackgroundScale);
this.differentImageBackground.setScaleX(newBackgroundScale);
this.differentImageBackground.setScaleY(newBackgroundScale);
How could this be achieved? I'm sorry if I don't understand this straight away - I've never done anything like this yet and some concepts are a bit difficult for me to get my head around them.
This a very common problem, and one that has several answers that might or might not be the best for your particular case, here are 2 from the top of my mind:
Develop a plugin architecture for your app, to load content and functionality from plugins Plugins architecture for an Android app?, note that this might be overkill if you just need to change a few classes here and there.
Modify your library project's architecture: This is one that I tend to use the most, just because it is simply and doesn't require a very complex refactoring. The steps needed for this are usually like this:
a. Figure out which parts of your activity or fragment might need to be extended by your main app project
b. Create interfaces and classes that implement those interfaces for the extendable functionality
c. This is the tricky part, isolate the creation and use of those classes in specific methods inside your activities or fragments
d. Finally on your main app project, create new classes that implement the same interfaces and override your fragments or activites to create these classes instead
I hope this helps you a bit, and if it doesn't you might want to sketch out some code in order to see exactly what problems you are having
This looks like a good fit for "inversion of control" design pattern. If a ExtendsActivity class is not changed between projects, but sometimes it uses an actor of class MyActor and sometimes ExtendsMyActor, then you should prepare a way for ExtendsActivity to accept the reference to such actor. You can inject this reference on construction, or later during the lifecycle of activity.
It is often recommended to use interface, i.e. define IActor and have both MyActor and any alternative implements this interface. But in some cases, extends fits perfectly, too.

Handling a core project and multiple derived from it in Android

I've searched around SO for this and found a few things, but I'm still not sure I fully understand, so I ask you for clarifications.
Here is what I need:
Have a project that has specific function: interrogate web service, display results in different views
Have a second, third and forth project that has exactly the same functionality as the first one, but only different graphic elements like splash screen image, icon, name, package name.
So, I have ProjectCore with activities and functionality. Project1 with a car icon and car image for splashscreen. Project2 with airplane icon and airplane image for splashscreen. Something like that. Each projects has a class with constants like'appId, appName, appServerURL"... All the web service call, data display is in Core as it's the same for all prohects, only the read is made from Constants class.
I was thinking of this approach
Make ProjectCore a Library project with a package like com.domain.core and dummy images
Make Project1, add reference to ProjectCore in it and with a package like com.domain.code.project1 and in resources folder, put images with same name as in core project
Make Project2 on the same principle like project1
Will this approach work ?
Thanks.
Later Edit. I've tried as mentioned before. For instance in Core project I had in drawable a file called splash.png. In Project1's and Project2's drawable folder I've put spash.png file with other images. This works fine. Running the Project1 and Project2 on my phone, started each app with it's own image. So far so good.
Then, because I have different constants I need to use in my App, I went into Core library project and added:
public class C {
public static String SomeConstant = "Project core!";
}
Here comes the problem, I need to have different constant values across Project1 and Project2. Because on Core project, the class is in com.domain.core.utils for instance... I can't add the same package in Project1 and Project2. How do I add the classes so I can update their values and be used on each project with particlar values ?
public class C {
public static String SomeConstant = "Project 1 constant!";
}
public class C {
public static String SomeConstant = "Project 2 constant!";
}
Thank you!
You want to create your functionality in a Library project and then have all of your Branded/OEM/3rdParty projects extend from this, overriding images and string resources where necessary.
When you need to use "Constants" you should instead have a single "run once" portion of your code (such as a splash screen) load these strings from resource files:
public static final String CONSTANT_ONE;
public void onCreate() { CONSTANT_ONE = getResources().getString(R.String.CONSTANT_ONE); }
EDIT
I'm unsure on how initialising a final value on onCreate() will perform. If final doesn't work well and you're worried about changing the variable during program execution then make the variable private (so only that class can assign to it) and then create a public static String getConstantOne() function.
Yes. Library projects are ideal for this, especially if only resources differ. I've used the exact approach that you've outlined with success...
Yes this should work fine. I did something a bit similar and I found occasionally you may have some circumstances where you want to call out from your library project to your application project. In these cases I used interfaces/abstract classes defined in the library project but implemented in application project...

Android - Multiple .apk file from single code base

I had developed 3 applications in android where the major functionalities are the same but the UI looks different. Images and the background color of the screens are different.
NOw, i want to create a single code base from which i can generate multiple .apk files for the 3 apps.
I tried creating 3 different packages for src folder for the 3 apps. But i dont know how to set the res folder for these apps.
Need pointers on creating a single code base from which we can generate multiple .apk files which includes only the respective src and res folders.
Use an Android Library Project that contains all your common code.
Create separate Android projects that reference the Library Project (you will need to copy your Manifest into each of these and make sure all components are declared with their full Java package name).
Put any resources specific to each app (drawables, colors etc) into the individual project resource folders and they will override similarly named resources in the library project at build time.
i think the best option is to use ant, you'll need to add an ant target for each build and change the resource folder.
if you use the generated build.xml, the res folder is defined like this
<property name="resource.absolute.dir" location="res" /> so you'll want to override that
Can't you put all of your common code into a library project and then just reference that project from each of the 3 unique projects that each contain the relevant resources.
Update: This answer is now obsolete when using the Gradle build system.
Why don't you use a single application, that does three different things based on SharedPreferences values set by the user, or from context at install time. If you really want to separate, you can have three different activities, and you decide which one to launch from a silent main Activity that redirects to either of the different ones.
An alternative is to have a unique activity that inflates itself dynamically from 3 different layouts at onCreate time.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (...custom check for layout... equals(layout1)) {
setContentView(R.layout.main_layout1);
} else if (... equals(layout2)) {
setContentView(R.layout.main_layout2);
} else if (... equals(layout3)) {
setContentView(R.layout.main_layout3);
} else {
throw new IllegalStateException("Unknown layout!");
}
... your onCreate stuff....
}
It will make code maintenance easier (only one code source to modify, only one version-list and changeset to maintain)
Check here:
How to use SharedPreferences in Android to store, fetch and edit values
I would suggest using Gradle flavors.
It seems to explain all the basics really well. I just finished converting to Gradle today, and it works great. Custom app icons, names, and strings, etc.
As the website explains, part of the purpose behind this design was to make it more dynamic and more easily allow multiple APKs to be created with essentially the same code, which sounds similar what you're doing.
Also see a recent question I had, referring to your project structure and using custom code for each app.

Calling non-library code from an Android library

Since Android introduced library projects, I've been converting my app into a library so that I can make several versions with appropriate tweaks (for example, a free and pro version using the same code base, but changing a few things).
I initially had trouble allowing the library project's code access to fields in my sub-projects. In other words, my free and pro versions each had a class with a few constants in them, which the library project would use to distinguish certain features.
In the sub-project, I extended the library's main activity and added a static initialisation block which uses reflection to change the values of fields in the library.
public class MyMainActivityProVersion extends MyMainActivity {
public static final String TAG = Constants.APP_NAME + "/SubClass";
static {
try {
ConstantsHelper.setConstants(Constants.class);
} catch (Exception e) {
Log.d(TAG, "--- Constants not initialised! ---");
e.printStackTrace();
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
In this code, ConstantsHelper is in the library, and I am providing Constants.class from my sub-project. This initialises the constants in the library project.
My approach works great, except for one particular use case. When the app hasn't been used in a while and it is 'stopped' by the OS, the static fields in ConstantsHelper are forgotten.
The constants are supposed to be reset by the main activity (as shown above), but the main activity isn't even launched because the OS resumes a different activity. The result of this is that the initialisation of the constants is forgotten and I cannot re-initialise them because the resumed activity is in the library (which has no knowledge of the sub-project).
How can I 'tell' other activities in the library to call code from sub-projects on resuming? Alternatively, is there a way to ensure that some code in my sub-project is called on every resume?
I think you're "cheating" by trying to share data across two Activities through static members. This happens to work when they're in the same, or related, classloaders. Here I believe Android uses separate classloaders for separate Activities, but, child Activities are in child classloaders. So ViewActivity happens to be able to see up into the parent classloader and see statics for the parent. Later I believe that parent goes away, and so your child re-loads MyMainActivity locally when you next access it and it's not initialized as you wanted. (Well, if it's not that, it's something very like this explanation.)
I think there are some more robust alternatives. You could use the LicenseChecker API to decide whether you're in a free or for-pay version rather than rely on details of the activity lifecycle and classloader. That's probably going to be better as it protects you from other types of unauthorized use.
I'm afraid I never found a good answer to this question. I'll probably continue with my terrible use of reflection and figure out some hacky workaround.
I felt I should come back and at least point out that I didn't solve this for the benefit of others who come to this page.
You can resolve this using Android resources.
Basically, define your constants in a resources xml values file in your Library project
E.g. "lib project"\values\constants.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<bool name="const_free_version">false</bool>
<string name="const_a_constant">pippo</bool>
</resources>
Then, in your sub-project you can redefine the lib-project values using a different resources xml values file:
E.g. "sub project"\values\constants.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<bool name="const_free_version">true</bool>
</resources>
In your lib project code when you refer to R.bool.const_free_version you get the actual value based on sub-project constant values xml.
Note that you don't have to redefine every values defined in the lib project constants.xml but only the ones you need different in your sub project.

Android using AndroidAnnotations in Library Project Eclipse

I'm making a paid/free version of my app so have a 'Library Project' that the two apps use.
I'm trying to use Android Annotations to clean up my code:
http://code.google.com/p/androidannotations/
Unfortunately when I use this in my shared library project, one of my projects gets the error in Eclipse:
The type xActivity_ is already defined xActivity_.java /ProjectName/.apt_generated/lib/activities/
Because Android Annotations automatically creates a new activity with an extra '_' in the folder .apt_generated one of the apps is allowed to create this file, but the other gets the error "already defined".
Is there a way in Eclipse to resolve this? Or is it a problem with the Android Annotations?
This seems to be an AndroidAnnotations bug, and should be reported on the dedicated bug tracker.
AndroidAnnotations wasn't designed with this use case in mind, but this is still a very valid use case. The problem seems to be that the activity is generated in the shared library project, when it should be generated in each depending project, am I right ?
(please answer in the bug tracker)
This question is quite old, but I thought that I should mention android annotations now supports being used in libaries:
https://github.com/excilys/androidannotations/wiki/Library-projects
One caveat is that due to the way android library projects generate the R class, you cannot reference resouces directly inside the annotations. Eg, you cant do this:
#EActivity(R.layout.myLayout)
public class MyActivity extends Activity {
#Click(R.id.myButton1, R.id.myButton2})
public void someButtonClicked() {
}
}
Instead you must do this:
#EActivity(resName="myLayout")
public class MyActivity extends Activity {
#Click(resName={"myButton1", "myButton2"})
public void someButtonClicked() {
}
}
I just knew AndroidAnnotations (which seems a great tool!) but I think that if you do this using different projects (sharing the same library) your problem should be solved.

Categories

Resources