I have a value for a String in String.xml
<string name="id">4</string>
and I have a class which contains a variable
public static int Id=1;
Now what I need is I want to get either of these two values in the gradle, which will check a condition and based on the condition it will rename my app. Below given is the part of the gradle code
def identifier //here i need to get value from the java or xml
switch(identifier)
{
case 1:
temp="ApplicationNewName";break;
}
newName=newName.replace("-release",temp);
output.output.File=new File(output.outputFile.parent,newName);
My question is that, Can i access the variables initialised in the java file or string xml in gradle file ?
You're approaching this problem backwards, Gradle gives you the ability to set those variables within the script itself and then you can further access those variables throughout your Android code. Here's a relevant answer for how you can set build configuration variables: https://stackoverflow.com/a/17201265/2168085
It also sounds like you are trying to build different apps from a single code base, or build variations of those apps. If that's the case then you should really look into build flavors to solve this problem. Essentially a build flavor allows you build different apps from a single main code base and apply variations or new functionality to the different flavors. This can be as basic as having a free and paid version of an app or a full white label code base where you can build very different apps from the same master code base. In Android these are more commonly known as build variants and the developer documentation gives plenty of good information on how to get started: https://developer.android.com/studio/build/build-variants.html
Related
I am investigating custom annotation processors for Android applications.
I have a use case where I would like to be able to use an annotation processor to read/amend the AndroidManifest.xml and add the following intent section for all Activities mentioned there in?
<intent-filter>
<action android:name="com.my.package.name.my_activity.open"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Is this possible?
It is a very interesting question but I don't think you could achieve such a task with an annotation processor since those work at Kotlin/Java code generation level.
How would you annotate XML #Something and have it still be valid XML?
Take a look at this:
KoltinConf18 - Annotation processing in Kotlin
At 7:18 Zack goes over annotation processing in Java and it basically says:
Happens at compile time
And you cannot modify code, just generate more
So by using barebones annotation processing you can't really modify the already existing AndroidManifest.xml.
An alternative would be writing a Gradle plugin that generates those bits of XML and merges it with the current XML file that already exists within the project.
Something from the top of my head could be:
Create an annotation and mark all activities that you want to introduce that bit of code
On the plugin side; when you are writing the Gradle task; you may use reflection and figure out which classes are annotated by such extension. Or just make the programmer put those activities in a specific directory inside the source folder, which would be way easier
With the fully qualified class names, you may look at the <activity> nodes in the AndroidManifest.xml, filter out the class names that don't match the list of annotated class names
Modify those nodes with the piece of code you would like to inject.
To get started on how to write a Gradle plugin take a look here
A simple example to get you started could be:
Step 1
You create a separate module to write your plugin if it gets too cumbersome but for this simple example I decided to stick it right in the build.gradle.kts. It doesn't need to be a kotlin Gradle file, but I am more proficient in Kotlin than in Groovy :)
As you can see I have created a text testFile.txt in the root of the project.
In code I just navigate to it and read it; print it's content and then modify it.
You could do the very same thing with your AndroidManifes.xml. Then you would need to recursively iterate over the source files from your srcDir looking for all of those activities annotated by your special annotation and store all of the fully qualified class names inside a List<String>. Then do the necessary replacements inside the AndroidManifest
Note that with this basic configuration the Gradle task appears in the Gradle tab inside the others category, to change that is a little bit off of the scope of annotation processing.
Step 2, profit
It works, as you can see the file has been updated and the println statements show the previous content of the file before modifying it
You could have a template AndroidManifest_template.xml then using a gradle task go through the AndroidManifest_template.xml and generate the real AndroidManifest.xml which would be used to build the app.
In other words, AndroidManest.xml would be a transient part of the build and you could use any XML preprocessor you want to take the template and turn it into the real file.
In case you want to add these intents conditionally depending on flavour of your app, you could use gradle flavours and manifest merging to achieve this - read more about flavours at https://developer.android.com/studio/build/build-variants
Also refer to following question for example of using gradle to modify manifest
https://stackoverflow.com/a/22759572/9640177
I'm developing on Android and have some difficulty with Gradle's build logic.
I'm trying to have a set of resources and java files for a combined flavors.
I put them in a folder src/productFlavor1ProductFlavor2/
But when I compile the build variant productFlavor1ProductFlavor2DevDebug , it simply doesn't get anything from the productFlavor1ProductFlavor2 folder.
I tried pretty much everything...
From the syntax and order:
src/productFlavor1ProductFlavor2/
src/productFlavor1productFlavor2/
src/productFlavor1-productFlavor2/
src/productFlavor1/ProductFlavor2/
To trying to indicate the folder to gradle
android.applicationVariants.all { variant ->
if (variant.getProductFlavors().get(0).name.equals('productFlavor1')
&& variant.getProductFlavors().get(1).name.equals('productFlavor2'))
variant.sourceSets = 'src/productFlavor1ProductFlavor2'//read only... so doesn't work
}
Anyone knows why the combination of flavors indicated in https://developer.android.com/studio/build/ doesn't actually work....? Or if I'm missing somethine there...
To make this work you need to define multiple dimensions of flavors.
It is not allowed to combine the single flavor dimensions.
I don't think it is a good idea to put source code to multi-flavor folders anyway as you are getting too many combinations to take care of.
If I were on your place, I would treat every dimension separately instead ie:
have a separate folder with productFlavor1 & productFlavor2.
Check this out: flavors, overview of build system.
The priority order for the different folders:
1. build variant (fully qualified name, like debugFlavor1Flavor2/src)
1. build type (debug/src)
1. product flavor (if you have multiple dimensions they ordered in order of declaration)
1. main/src
My Problem might not be valid. And the points I mention here might be little incorrect as I am neither perfect nor expert.
I have a shopping application and I want to start building a modular application. Like I want to add Affiliate User( the module which adds certain functionality or this will display some extra pages in an application) in the application.
A similar situation happens in the Frameworks: We add and enable the certain module and in return framework load everything as required.
for this, I want following changes like:
add an entry in the NavigationView displaying "Affiliate Label".
load fragments (just adding one more fragment for one more label/option from navigation view).
Let's say I have a library project that contains a Fragment and all relevant code.
How can I build the application automatically let's say just by writing "true" somewhere in the XML?
Automatically here means label is added, Intents are performed on click of label etc.
<Modules>
<enable>true/false</enable>
</Modules>
This is just the simple scenario.
You could do this (like everywhere when it comes to writing code) in many possible ways.
The "file" way:
Make a new file named something like modules.txt with key value pairs. Load the file and check whether a module is enabled or not.
The "Constant" way:
Make an abstract class which only contains public static final variables which describe your modules.
The "package manager" way:
See create Android Application plugins/extensions (apk)
The "multiple" apk way:
Note that this is not reccomended!
we encourage you to develop and publish a single APK
multiple apk support
To add to codewing's answer, you can also use Gradle's resource management capabilities to accomplish this, so you only ever need to look in one place for an enabled/disabled status.
For this, you have 2 solid options.
The first starts with a boolean which can be split by flavor:
<bool name="module_x_enabled">true</bool>
The second would be to inject your values into a String resource after Gradle merges the resources by adding something like this to your build.gradle file, then comparing that enabled value:
<string name="module_x_enabled">MODULE_X_ENABLED_PLACEHOLDER</string>
android.applicationVariants.all{ variant ->
variant.mergeResources.doLast{
replaceInValues(variant, 'MODULE_X_ENABLED_PLACEHOLDER', MODULE_X_ENABLED)
}
}
def replaceInValues(variant, fromString, toString) {
File valuesFile = file("${buildDir}/intermediates/res/merged/${variant.dirName}/values/values.xml")
String content = valuesFile.getText('UTF-8')
content = content.replaceAll(fromString, toString)
valuesFile.write(content, 'UTF-8')
}
Where MODULE_X_ENABLED would be a setting in your gradle.properties file like:
MODULE_X_ENABLED=true
Edit: or better yet,
Why not pull the settings from some kind of server so that you don't need to rebuild and relaunch to update a client's module?
I have inherited an old android app, and the first thing I did, was change the build from Ant to gradle.
The app builds fine, but when I try to run the release target, gradle fails complaining about missing translations.
The folder structure of the resource folder is: (the default language, in the values folder is en-GB)
res/
values/
/file1.xml
/file2.xml
/file3.xml
/values-us/
/file2.xml (containing just the few lines that differ from /value/file2.xml
/values-dk/
file1.xml
file2.xml
file3.xml
So, basically the linter is correct, most of the files are missing in the values-us folder.
However, a single locale contains about 20 files, with around 100+ lines each, and the difference between the default and US is probably 5 lines total, it seems impractical to have to maintain all values for both locales.
To be clear, I absolutely want the release target to fail, when values-dk/ or values-de/ are missing translations, I's just like some way to mark the US locale as an extension of the default. Is this possible?
I think Lint found some strings that are not in res/values but in res/values-{language}. In this case, if the code tries to use one of those strings and the string selection falls to default one (phone is set to a language where you don't provide translations), the application will crash.
The error should tell you which string is not translated. I'd start by checking the languages defining it and make sure it's also in the default ones.
So it turns out this is actually pretty simple. All I had to do was rename the values-us/ folder to values-en-rUS/ and it works.
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.