Share Resources Among Multiple Android Source Sets - android

I am currently working on an Android project with multiple source sets.
My question is concerned with string resources.
The majority of the string resources are in the main/res/values directory.
There is an alternative source set called foo which overrides some of the string resources in main/res/values. This works just fine, however there is an additional source set we can call foobar that is a slightly different version of foo.
Is there a way foobar can be configured to use the resources defined in foo/res/values instead of defaulting back to main/res/values? Despite the source sets both being slightly different, the resources between foo and foobar are to be identical so I'd only like to write them once.
Essentially in foobar if I try to get the string resource cat I want it to look in foo/res/values/string.xml as if I was making the resource reqeust in foo; and just like in foo; fallback to main/res/values if that resource isn't defined.
Is there a way that I can structure my project to have this behavior? I am limited in how much I can restructure the source sets within the project, so I understand that the problem I am presenting might go against some conventional practices.

The solution to my problem was to add the following to my application's build.gradle
android.sourceSets {
foobar.res.srcDirs = ['src/foo/res']
}

Related

Android resources key collision

I have two Android projects, a main one (package name com.adip.sampler) and a library that is added to main (package name com.samples.projb). In both of them in resources I have an integer-array with same key: my_int_values:
In main project:
<integer-array name="my_int_values">
<item>10</item>
<item>20</item>
<item>30</item>
<item>40</item>
<item>50</item>
<item>60</item>
<item>70</item>
<item>80</item>
</integer-array>
while in library:
<integer-array name="my_int_values">
<item>34</item>
<item>35</item>
<item>36</item>
<item>37</item>
</integer-array>
In main project from an activity if I am investigating what are the values from these arrays (both main project and library):
protected void showLocalStrings() {
Log.d("RESSampler", "In Main: " + Arrays.toString(getResources().getIntArray(com.adip.sampler.R.array.my_int_values)));
Log.d("RESSampler", "In Libr: " + Arrays.toString(getResources().getIntArray(com.samples.projb.R.array.my_int_values)));
}
then I'm seeing this in Logcat:
In Main: [10, 20, 30, 40, 50, 60, 70, 80]
In Libr: [10, 20, 30, 40, 50, 60, 70, 80]
It seems that main project is overriding the values defined in library array ... I doubled checked if I am reading from resources with correct key and that is ok. Until I took a look in each generated R class. In the main project this is what I have for com.adip.sampler.R.array.my_int_values:
public static final class array {
public static final int my_int_values=0x7f060000;
}
while in library project com.samples.projb.R.array.my_int_values:
public static final class array {
public static final int my_int_values = 0x7f060000;
}
Android tool has generated the same value, so no wonder I am getting this behavior. I can get rid of this behavior if I change the key from one of the integer arrays, but imagine that you have some big projects with a lot of resources, dependency libraries and sooner or later you may bump into this kind of issue: having the same type of resources with the same key value (I've checked with string and with string-array and above behavior appears there as well). So the questions would be:
Why this issue appears? Or if it's not an issue what explains this behavior?
How to avoid it best? I am guessing that trying to have some kind of uniqueness in defining the keys will do the trick, but developers tend to be lazy ...
This appears using multiple variants of latest ADTs and Eclipse versions (Juno and Indigo). Checked on Windows only.
Reading from Library Projects at Android Developers, there are many references where they clearly say merging happens at build time and resources with same IDs overwrite each other.
For resources with same ID from a library and application
In cases where a resource ID is defined in both the application and
the library, the tools ensure that the resource declared in the
application gets priority and that the resource in the library project
is not compiled into the application .apk. This gives your application
the flexibility to either use or redefine any resource behaviors or
values that are defined in any library.
For resources with same ID from two libraries
... your application can add references to multiple library projects, then
specify the relative priority of the resources in each library. This
lets you build up the resources actually used in your application in a
cumulative manner. When two libraries referenced from an application
define the same resource ID, the tools select the resource from the
library with higher priority and discard the other.
Solution suggested in documentation
Use prefixes to avoid resource conflicts
To avoid resource conflicts for common resource IDs, consider using a
prefix or other consistent naming scheme that is unique to the project
(or is unique across all projects).
How to set priority in libraries from command line
If you are adding references to multiple libraries, note that you can
set their relative priority (and merge order) by manually editing the
project.properties file and adjusting the each reference's .n index as
appropriate.
Personally, I would change the names of the resources. If you read anything about naming conventions, it should be something meaningful and "my_int_array" isn't really too helpful, especially in a library that other people or project could potentially use.
Ideally, you want to be able to forget about this for 6 months, come back and look at it and know what that array is for/contains, without having to delve through code to deduce what the array is for by what's done with it.
This post; https://stackoverflow.com/a/7249410/1222199 contains a few different answers on naming conventions.
Finally, not sure about the conflict, couldn't dig anything up. I'm guessing it's to do with the way it's automatically generated, might be worth logging it as a bug and see if you get any response from the development team.

what R.java file actually does and how

I have been working on a simple android tutorial and while browsing through the project folders I found this R.java file in gen folder...
When I opened it seemed to me as a mess...
first R itself is a class.
it had multiple Inner classes defined within eg drawable,id,layout,etc.
and that inner classes had lots of variables declared as below which were assigned with hex values
public static final int addr=0x7f080003;
...
...
and much more
R is auto generated and acts as some pointer for other files
Questions for R.java
what it is basically for
how it works
why
values are in hex
what role did it performs while the actual application is running
"Acts as some pointer to other files" is actually absolutely correct, now the question is which files it points to how it is done.
What does it contain?
R file contains IDs for all the resources in the res folder of your project and also some additional IDs that you define on your own (in the layouts, for example). The IDs are needed for the Android resource management system to retrieve the files from the APK. Each ID is basically a number which corresponds to some resource in the resource management system.
The file itself is needed so you can access or reference the resource from code by giving the ID of the resource to the resource manager. Say, if you want to set the view in the activity, you call
setContentView(R.layout.main);
main in the R file contains the number which is understood by the Android resource management system as the layout file which is called main.
Why is it better than just plain file names?
It's harder to make a mistake with the generated fields. If you write the field name incorrectly, your program won't compile and you will know that there's an error immediately. If you write an incorrect string, however, the application won't fail until it is launched.
If you want to read more on this topic, you should check the Android documentation, especially the Accessing Resources part.
This holds your resource ids. So when you do something like
TextView tv = (TextView) findViewById(R.id.mytextview);
it looks up your id here for that View, layout, etc... This way the app has an easy way to look up your ids while you can use easy to remember names. Anytime you create a resource it automatically creates an id for it and stores it here. That's why you never want to try and edit this file yourself.
One way to think about how valuable R.java is, imagine a world without it. Its amazing how android brings the xml and java world together to help avoid coding the UI manually completely. With legacy java building UI using the java language was a pain. Invaluable.
With Android you can not only build your UI using only xml, but also see it while you build it. Invaluable.
Every element in the xml can be referenced in the java code WITHOUT writing a single line of code to parse the xml :). Just R.id.nameOfElement. Invaluable.
Rapid development is beautifully done in android. Imagine if iPhone would have 5000 screens to fit that one piece of code, they would crumble on their XCode. Google has done a wonderful job with just R.java. Invaluable.

Android String File equivalent in iPhone(Monotouch)

In android we have files by name String. Developers define the string values that they used for naming objects in this file. This is a very useful way. Because avoid of hard coding string values(you can change them from a single file, less time to change), also useful to creating multi language application and etc. (for more info just google this).
But the question is this: whether iPhone(Monotouch) have a mechanism like this to define strings on them or developers have to define themselves mechanism for this?
In XCode, you'll find File/New File, then on the left, pick "Resource", and you'll find "Strings File".
From code, you'll be referencing the keys in your strings file with NSLocalizedString:
NSLog("%#", NSLocalizedString(#"YOUR-STRING-KEY-OR-DEFAULT-VALUE", #"Comment about what this is"));
For details on what that second param is for, What is the second parameter of NSLocalizedString()?
Put your strings in a single file. Make them global constants. Access them throughout the app. When you change these, the change will be reflected everywhere.
It's not a big deal to have persistent string references throughout your app. It can be done in any decent programming language and platform I suppose.

Android: Is it possible to alias R from app and lib to distinguish between the two?

Can I somehow alias a generated R file from a library and a generated R file from an application?
IE: I have two projects, one being a library. I reference the library in the application.
The library creates one R file, the application creates two: com.example.mapplication.R and the R from the referenced library, com.example.mlibrary.R. However, since I want to use both frequently in the application, I want to alias both the R classes. For example, call the library R something like libR and the app R something like appR.
I tried a couple of things which are of such stupidity, I won't even fully mention them, but I did some things like private com.example.mapplication.R appR = com.example.mapplication.R (immediately realising this wasn't gonna work), and private Class<com.example.mapplication.R> appRclass = com.example.mapplication.R.class;, but that didn't give me the desired effect. I do, however, use the latter method and some reflection to make it possible to be able to get the field and inner classes like this:
Class<com.example.mapplication.R> appRclass = com.example.mapplication.R.class;
int activity_main_layoutid = Toolkit.getFromClass(appR).
getMeTheInnerClass("layout").getMeAField("activity_main");
I haven't fully implemented the Toolkit method, but as we all know refletion can do this kind of stuff, but at the end of the day it's just as much work as just typing com.example.mapplication.R or com.example.mlibrary.R everywhere.
So to wrap thing up: Is it possible to alias multiple R's so that I can use appR and libR to distinguish the two?
I wish we could, but according to what I know, it isn't possible, at least not yet.
I also wish we could put resources in sub folders, but even this is impossible.
What you can do is setting a unique prefix (rename each resource file) for each library, as it's done by some third party libraries (like actionBarSherlock, which uses "abs" as the prefix).
Alternatively, you can have a special trick and put the files in the src folder and reach them from there, but that's more of a workaround, since the R won't have a reference to any of the resources there.
For example, you can put an image called image.png into the src/com/company/app_name, and then create a bitmap from it using:
final InputStream fis = getClass().getResourceAsStream("/com/company/app_name/image.png");
final Bitmap bitmap = BitmapFactory.decodeStream(fis);
By the way, if you've thought of using the assets folder, you can't do it for library projects, since it requires you to have the files in the project that uses the library project. It will work, but it misses the encapsulation idea...

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.

Categories

Resources