Gracefully handle missing gradle definitions (use default values if definition is missing) - android

I usually do following in my projects:
1) define a versions.gradle file, e.g.:
ext {
setup = [
compileSdk: 28,
enableDataBinding: true,
minSdk : 16,
targetSdk : 28
]
androidx = [
supportv4: "1.0.0",
appcompat: "1.0.0",
cardview: "1.0.0",
viewpager: "1.0.0",
material: "1.0.0"
]
}
2) I add this versions file to my projects gradle file:
apply from: './versions.gradle'
3) I use the versions from the file in all my project gradle files for consistant library versions, like e.g.:
implementation "androidx.appcompat:appcompat:${androidx.appcompat}"
Question
How can I handle this gracefully in open source libraries I use? I want that if someone checks out my library and adds the library directly to his projects, that some default version is provided so that this line implementation "androidx.appcompat:appcompat:${androidx.appcompat}" won't throw any error (${androidx.appcompat} should be replaced by 1.0.0 e.g. in this case).
Is there some way to solve this easily? I want that the version file is used if available and a fallback version is used otherwise...

Trying Elvis operator for placing default value can do that trick, so replacing it with operator like below works :
implementation "androidx.appcompat:appcompat:${androidx.appcompat ?: 'default value here'}"
More from here.

Related

How to generate OpenAPI sources from gradle when building Android app

What I'm trying to achieve
I'm trying to generate my REST API client for Android using OpenAPI Generator from the build.gradle script. That way, I wouldn't have to run the generator command line every time the specs change. Ideally, this would be generated when I build/assemble my app, and the sources would end up in the java (generated) folder, where generated sources are then accessible from the code (this is what happens with the BuildConfig.java file for example).
What I've tried so far
Following this link from their official GitHub, here's the build.gradle file I ended up with:
apply plugin: 'com.android.application'
apply plugin: 'org.openapi.generator'
...
openApiValidate {
inputSpec = "$rootDir/app/src/main/openapi/my-api.yaml"
recommend = true
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/app/src/main/openapi/my-api.yaml"
outputDir = "$buildDir/generated/openapi"
groupId = "$project.group"
id = "$project.name-openapi"
version = "$project.version"
apiPackage = "com.example.mypackage.api"
invokerPackage = "com.example.mypackage.invoker"
modelPackage = "com.example.mypackage.model"
configOptions = [
java8 : "true",
dateLibrary : "java8",
library : "retrofit2"
]
}
...
First, I've never managed to get the API generated with the build/assemble task, even when I tried adding:
compileJava.dependsOn tasks.openApiGenerate
or
assemble.dependsOn tasks.openApiGenerate
The only way I could generate the sources was by manually triggering the openApiGenerate task:
Then, when I do generate my sources this way, they end up in the build folder but aren't accessible from my code, and aren't visible in the java (generated) folder:
I then have to manually copy/paste the generated source files to my project sources in order to use the API.
Even though I'm able to work around these issues by adding manual procedures, it would be way more maintainable if the whole process was simply automatic. I was able to achieve a similar result with another tool, Protobuf. Indeed, my gradle task gets triggered every time I build the app, and the sources end up in the java (generated) folder, so I don't have to do any additional work. The task is much simpler though, so I assume the main work that I'm not able to replicate with OpenAPI Generator is handled by the Protobuf plugin itself.
You have to specify path to the generated sources as a custom source set for your Gradle module, which is app in this case, as described here – https://developer.android.com/studio/build/build-variants#configure-sourcesets. That way Gradle will treat your sources as accessible from your code.
Something like this:
android {
...
sourceSets {
main {
java.srcDirs = ['build/generated/openapi/src/main/java']
}
}
...
}
I solved the issue you described like this, I'm using gradle.kts however.
See my build.gradle.kts
plugins {
// Your other plugins
id("org.openapi.generator") version "5.3.0"
}
openApiGenerate {
generatorName.set("kotlin")
inputSpec.set("$rootDir/app/src/main/openapi/my-api.yaml")
outputDir.set("$buildDir/generated/api")
// Your other specification
}
application {
// Your other code
sourceSets {
main {
java {
// TODO: Set this path according to what was generated for you
srcDir("$buildDir/generated/api/src/main/kotlin")
}
}
}
}
tasks.compileKotlin {
dependsOn(tasks.openApiGenerate)
}
You need to build the application at least once for the IDE to detect the library (at least this is the case for me in Intellij)
Your build should automatically generate the open api classes , to refer the generated classes in your java project you should add the generated class path to your source directory like it was mentioned in the other answers
https://developer.android.com/studio/build/build-variants#configure-sourcesets
As far as the task dependency goes , in android tasks are generated after configuration thus for gradle to recognize the task , wrap it inside afterEvaluate block like
afterEvaluate {
tasks.compileDebugJavaWithJavac.dependsOn(tasks.openApiGenerate)
}
I had this issue, and this answer https://stackoverflow.com/a/55646891/14111809 led me to a more informative error:
error: incompatible types: Object cannot be converted to Annotation
#java.lang.Object()
Taking a look at the generated files that were causing this error, noticed:
import com.squareup.moshi.Json;
After including a Moshi in the app build.gradle, the build succeeded and the generated code was accessible.
implementation("com.squareup.moshi:moshi-kotlin:1.13.0")

Use MotionLayout and ConstraintLayout dependencies with different versions

I've been using ConstraintLayout of version 1.1.2 for a while now. It was working perfectly. Then new MotionLayout came up and I thought why not to try it out. And everything seemed fine.
However I made a mistake of using it in production. Only after some time I noticed some bug reports on ConstraintLayout working not properly. But there are some screens already that depend on MotionLayout, removing which will cause a lot of refactoring.
Is it possible to use MotionLayout(v2.0.0-alpha-05/beta-02) and ConstraintLayout(v1.1.3) for the same project, so that screens that work with MotionLayout would have v2.0.0 and screens that work with ConstraintLayout only would have v1.1.3? Is there some packaging tool to move MotionLayout to a different package? I tried to use shadowJar gradle plugin but failed because MotionLayout is an *.aar dependency not *.jar.
Edit:
I've created a sample where I use jetifier gradle plugin from aosp to rewrite the package names and demonstrate how to use both 1.1.3 and 2.0.0-beta versions in the same project.
You can use jetifier with custom config file to rewrite package name. Just run it on both constraintlayout-2.0.0-beta2.aar and constraintlayout-solver-2.0.0-beta2.jar like this:
./jetifier-standalone -i constraintlayout-2.0.0-beta2.aar -o myconstraintlayout-2.0.0-beta2.aar -c config.json
./jetifier-standalone -i constraintlayout-solver-2.0.0-beta2.jar -o myconstraintlayout-solver-2.0.0-beta2.jar -c config.json
where config.json is the custom config like this:
{
"restrictToPackagePrefixes": [
"androidx/"
],
"reversedRestrictToPackagePrefixes": [],
"rules": [
{
"from": "androidx/(.*)",
"to": "myandroidx/{0}"
},
],
"packageMap": [
{
"from": "androidx/constraintlayout/widget",
"to": "myandroidx/constraintlayout/widget"
}
],
"pomRules": [],
"versions": {
"latestReleased": {}
},
"map": {
"types": {}
},
"proGuardMap": {
"rules": {
"androidx/{any}": [
"myandroidx/{any}"
]
}
},
"stringsMap": {
"types": {}
}
}
You can check the original config file to find out file format.
After that you can use myconstraintlayout-2.0.0-beta2.aar and myconstraintlayout-solver-2.0.0-beta2.jar in your project. Obviously you'll have to change package name for MotionLayout in your project.
It should be possible to automate the process by writing a gradle plugin also.
Edit: it's probably better to repackage constraintlayout-1.1.3, so you can easily update MotionLayout with new versions when they are released.
I faced similar issues - the only possible way to avoid the class collision is to rebuild one of the dependencies with the different package name. It could be done by downloading the code of one of the libraries and recompile it manually on your PC. For example from here.
But I would recommend you to use ConstraintLayout library version 2.0
implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta2'
or
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
if you are using AndroidX.
Is contains both ConstraintLayout and MotionLayout so they would not create a conflict in your code. Some small adjustments may be needed in your current code though.
Hope it helps.

How to import OpenCV android-sdk to my project which is using Bazel?

I have been trying to import opencv-android-sdk to my Bazel project but I am not able to do it.
I tried the this answer on SO but while building my project I get errors that
error: package org.opencv.android does not exist
I see that there's an opencv-android artifact on Maven.
You can depend on this using rules_jvm_external.
In your WORKSPACE file, specify the dependency along with the other external dependencies:
load("#rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
artifacts = [
"org.opencv:opencv-android:1.0.1",
# ...
],
repositories = [
"https://maven.google.com",
"https://jcenter.bintray.com",
],
)
Then, in your BUILD file containing your Android targets, depend on the OpenCV target:
android_library(
name = "my_lib",
custom_package = "com.example.bazel",
srcs = glob(["java/com/example/bazel/*.java"]),
manifest = "java/AndroidManifest.xml",
resource_files = glob(["res/**"]),
deps = [
"#maven//:org_opencv_opencv_android",
],
visibility = ["//src/test:__subpackages__"]
)
Finally, you should be able to reference classes like org.opencv.core.Core in your Android Java code.
P.S. consider switching all your maven_jar and gmaven_rules/gmaven_artifact to use rules_jvm_external. The former Maven rules have been deprecated in favor of rules_jvm_external.

'ProductFlavor.resConfigs' has a value 'auto' which is obsolete and has not been replaced

How to fix the warning below? Are there any alternatives to 'auto'?
Warning:DSL element 'ProductFlavor.resConfigs' has a value 'auto' which is obsolete and has not been replaced. It will be removed at the end of 2018
android {
...
flavorDimensions "device", "paid", "market"
productFlavors {
phone {
// Phone config version for the application
resConfigs ("auto")
dimension "device"
matchingFallbacks = ['mobile']
}
...
}
...
}
This is the error after updating to Android Studio 3.1:
Based on the official advise here the best thing to do is removing the tag entirely if you support all the languages or supply an array with the language's code your app supports like:
resConfigs "en", "de", "it", "fr" // etc. etc.
More info:
This is one of the resources optimization proposed by the official documentation here so i decided to test this flag with those FirebaseUI dependencies in a sample project
implementation "com.firebaseui:firebase-ui-auth:$firebase_ui_version"
implementation "com.firebaseui:firebase-ui-database:$firebase_ui_version"
implementation "com.firebaseui:firebase-ui-storage:$firebase_ui_version"
creating the debug APK with both the options and those are the results:
Using resConfigs "auto" the debug APK was: 3,793 KB
Using resConfigs "en" (so 1 language only) the debug APK was: 3,294 KB
This means that with all the string resources for all the languages of those dependencies I got only ~500KB of size increase. That's something you could reason about, you definitely should make a test with the dependencies you use and see if the size increase is negligible or not and consequently decide to provide the list of supported languages or remove the resConfigs option.
PS: If you are using Android FirebaseUI this was one of the suggested optimizations, I've created an issue here about the thing and this has been solved immediately by the awesome guy called SUPERCILEX
auto is no longer supported because it created a number of issues with multi-module projects. Instead, you should specify the locale that your app supports. Android plugin 3.1.0 and higher ignore the auto argument, and Gradle packages all string resources your app and its dependencies provide.
com.android.tools.build:gradle:3.2.0-alpha08 BaseFlavor.java
* <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of
* issues with multi-module projects. Instead, you should specify the locale that your app
* supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>auto
* </code> argument, and Gradle packages all string resources your app and its dependencies
* provide.

update version number in README.md during gradle build

I am working on an Android library (aar) project. The project contains a README.md file which in turn contains these lines:
... declare library dependency:
Gradle: `compile 'com.acme:mylibrary:1.0.0#aar'`
My gradle.properties file contains:
VERSION_NAME=1.0.0
The problem is that currently I have to keep two files manually in sync. What I would like to do is keep the VERSION_NAME property and substitute it's value into README.md
If you have some pattern to find out where you used the version number it could be as easy as creating a tasks and replacing text based on a regex.
Something like:
task replaceVersionInREADME << {
// Maven
ant.replaceregexp(match:'<version>([0-9\\.]+)</version>', replace:"<version>${version}</version>", flags:'g', byline:true) {
fileset(dir: '.', includes: 'README.md')
}
// Gradle
ant.replaceregexp(match:'com\\.acme\\:mylibrary\\:([0-9\\.]+)', replace:"com.acme:mylibrary:${version}", flags:'g', byline:true) {
fileset(dir: '.', includes: 'README.md')
}
}
Change the regex as you need.

Categories

Resources