Android Modular Programming - android

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?

Related

Can you read/amend/replace AndroidManifest.xml file with a Custom Annotation Processor

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

How can I build an sbt plugin that stores the name of all implementations of an interface?

I'm trying to do the impossible and it doesn't seem to be working. The overall goal is this: I have a Component interface and I would like to show the user a list of classes on the classpath that implement that interface. The trick is, it has to run in Android.
Near as I can tell this is impossible to do at run time. The java mechanism (ServiceLoader) has been intentionally crippled by the Android toolchain, so it doesn't work. Guava doesn't work on Android, nor does ClassUtils, nor does Reflections.
At this point I've been yak shaving for 8 hours strait and there's no end in sight, so I'm looking for alternative approaches. My current thinking is to build a plugin (very much like sbt-spi, but not, because Android hates SPI) that can generate a text file at compile time that lists every class which implements the interface, so that at runtime I can open that file as a resource and then use reflection to start building them. Is that a reasonable idea? How should I go about it? (my current approach is "read the sbt-spi plugin source and try to copy it", but this seems like a scenario where "ask for wisdom" is a better approach)
Got it! I ended up using sbt-spi after all (huzzah not reinventing any wheels!) and just moving the output into the intermediate assets directory as part of the resourceGenerators task:
lazy val androidEntryPoint = (project in file("android-entry-point"))
.dependsOn(core, components, androidComponents)
.enablePlugins(SpiPlugin)
.settings(commonSettings: _*)
.settings(resourceGenerators in Compile += Def.task{
// This task copies the list of Components to the appropriate place to ensure
// it gets included in an accessible place in the APK
val res = collectResources.value._1 // item _1 here is for assets, item _2 is for resources. See the output of sbt "show androidEntryPoint/android:collectResources"
mapExport.value.toSeq.map { name =>
IO.move(target.value / name, res / name)
res / name
}
}.taskValue
)
That said, I'd love to hear a better approach if you can think of one. If none turn up in the next week or so I'll mark this one the answer.

Access a variable in Java or string.xml from Gradle

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

Can I design the UI in Xamarin.Forms by using XAML

I'm trying to create a cross platform app using Xamarin.Forms. As far as I know, the UI will be created from the code and the .axml file will be generated automatically.
Can I modify the .axml file to edit the UI? I tried editing but all that comes up is what is written in the code. ie: hello forms
UPDATE
public static Page GetMainPage ()
{
return new simplerow ();
}
In Xamarin.Forms you can create your pages from markup definitions that are shared across all platforms.
Typically you will write all your content pages using Xamarin.Forms, however you can mix-and-match native pages into an application should you so wish.
These shared common pages, written in Xamarin.Forms, will reside maybe in a PCL project, or a Shared Project so these can then be re-used in the platform-specific projects, each targeting a specific platform OS.
You can write these common pages, either in code, or in XAML. You can even choose to write some pages one way, and some the other if you so choose.
A Xamarin.Forms page is processed at runtime through the interpretation of the page composition that has been created.
Each control that is specified on a page, has its own platform specific renderer, behind the scenes, that will produce output that is targetted to that OS.
When writing Xamarin.Forms pages, for-the-most, you will start to learn a new way of creating pages that is abstracted from the intricacies of creating mobile applications on each different platform OS.
There is therefore no editable .axml that is generated etc as you will write your pages using Xamarin.Forms markup and controls, and even your own or other custom-controls to produce your own application pages.
The following link shows some examples of how to write XAML pages.
The following link shows some examples of how to write from code-behind pages.
Along with the previous answer re: .xaml instead of .axml, you need to remember to change the startup code in app.cs to use your new .xaml form. Replace the "new ContentPage {...};" with "new MyForm();" (where "MyForm" is the name of your shiny new XAML form).
EDIT: Downloaded the project from the dropbox link. Comments below...
I see several issues here. I think you may need to go through the walkthroughs and sample applications provided by Xamarin to get up to speed with the concepts behind XF apps.
First, you are trying to use an Activity as your application's page. In a Xamarin Forms app, it must be a View of some sort, not a platform-specific visual such as Activity.
Second, remove the "test.xml" file from your Android project's Resources/layout folder; while XAML files are indeed XML, they have an 1) have a file extension of .xaml and 2) belong in the shared project.
Here's what you need to do to get your project working: (I'm assuming you're using VS here, under Xamarin Studio, it's slightly different.)
Right-click your "testforms" shared project
Click Add from the context menu and select "New Item"
In the dialog that appears, select "Forms XAML Page" and in the Name area enter a name (such as "MyForm")
(If you're using XS, choose "New File" and "Forms ContentPage")
This will add two files to your project: a XAML file containing your layout (e.g.: MyForm.xaml), and a code-behind file (e.g.: MyForm.xaml.cs).
Open the XAML file, and modify the Label element so that the Text attribute is
Text = "Hello, World!"
Modify the body of GetMainPage in your App.cs to the following:
return new MyForm();
Run the app
Hope this helps!
You got it wrong. Forms are created either through code or XAML. No axml or anything persistent is generated at platform level, everything is done in runtime(XAML is sort of compiled at compile time).
So, modify either code or XAML if you wish to change something. Or, if you need something more demanding, than consider either subclassing an existing Renderer or create you own.

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