I have a kotlin multiplatofrm library that is included into an Android and iOS app.
In my android project include it as a composite build (MyLib). But Intellisense is not working at all for all code from in MyLib, though the whole thing compiles fine. I am using Android Studio. What could be wrong and how can I debug it?
rootProject.name='xxx'
includeBuild 'MyLib'
include ':common'
include ':app'
MyLib's build.gradle.kts looks as follows:
plugins {
kotlin("multiplatform") version "1.5.31"
kotlin("native.cocoapods") version "1.5.31"
}
repositories {
mavenCentral()
maven { setUrl("https://dl.bintray.com/kotlin/kotlinx.html/") }
}
group = "com.xxx.MyLib"
// CocoaPods requires the podspec to have a version.
version = "1.0"
kotlin {
ios()
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnit()
}
}
cocoapods {
ios.deploymentTarget = "11.4"
frameworkName = "MyLib"
summary = "xxx"
homepage = "xxx"
podfile = project.file("../../iOS-App/Podfile")
}
sourceSets {
commonMain {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31")
implementation("com.badoo.reaktive:reaktive:1.2.0")
implementation("com.badoo.reaktive:reaktive-annotations:1.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1")
implementation("com.russhwolf:multiplatform-settings-no-arg:0.8.1")
implementation("net.swiftzer.semver:semver:1.1.1")
}
}
}
}
tasks.withType<GenerateModuleMetadata> {
enabled = true
}
I think this is likely related to https://youtrack.jetbrains.com/issue/KTIJ-18903
I had the same issue and it drove me crazy. How can one write code nowadays without Intellisense (answer: you can't).
I tried a ton of things (all the usual and unusual stuff you do when Android Studio / IntelliJ act up). Ultimately I upgraded to Kotlin 1.6.0-RC2 (from 1.5.31) -> https://github.com/JetBrains/kotlin/releases/tag/v1.6.0-RC2.
Part of that is upgrading the Kotlin Plugin:
Another part is the Kotlin Gradle Plugin dependency:
org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0-RC2
And last but not least I had to downgrade the corouting dependency (from 1.5.2):
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC
After that everything was back to normal.
I'm trying to integrate Firebase on Unity3d in order to receive push notifications.
I get the following error when the app launching:
I/dalvikvm( 7881): Could not find method android.content.Context.isDeviceProtectedStorage, referenced from method com.google.firebase.FirebaseApp.zza
I/FirebaseInitProvider( 7881): FirebaseApp initialization unsuccessful
The firebase-messaging sample work fine with my google-services.json. So I suppose that the console configuration is correct.
1. I created a library project on Android Studio.
I followed a web site to build a .jar from Android Studio and the official documentation to add firebase.
http://www.thegamecontriver.com/2015/04/android-plugin-unity-android-studio.html
project gradle file:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.1'
classpath 'com.google.gms:google-services:3.0.0'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app gradle file:
apply plugin: 'com.android.library'
android {
compileSdkVersion 24
buildToolsVersion "24.0.3"
sourceSets {
main {
java {
srcDir 'src/main/java'
}
}
}
defaultConfig {
minSdkVersion 15
targetSdkVersion 24
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.google.firebase:firebase-core:9.6.1'
compile 'com.google.firebase:firebase-messaging:9.6.1'
compile files('libs/classes.jar')
}
task deleteOldJar(type: Delete) {
delete 'release/NotificationPlugin.jar'
}
task exportJar(type: Copy) {
from('build/intermediates/bundles/release/')
into('release/')
include('classes.jar')
rename('classes.jar', 'NotificationPlugin.jar')
}
exportJar.dependsOn(deleteOldJar, build)
apply plugin: 'com.google.gms.google-services'
At this point a .jar is successfully generated.
2. I created a JarResolver dependencies in Unity.
I use the PlayServicesResolver v1.1 found into other google unity package (Google analytics for exemple, other plugins upgrade it)
https://github.com/googleanalytics/google-analytics-plugin-for-unity
FirebaseDependencies.cs
using Google.JarResolver;
using UnityEditor;
[InitializeOnLoad]
public static class FirebaseDependencies
{
private static readonly string PluginName = "Firebase";
static FirebaseDependencies()
{
PlayServicesSupport svcSupport = PlayServicesSupport.CreateInstance(
PluginName,
EditorPrefs.GetString("AndroidSdkRoot"),
"ProjectSettings");
svcSupport.DependOn("com.google.firebase",
"firebase-core",
PluginVersion.PlayServicesVersionConstraint);
svcSupport.DependOn("com.google.firebase",
"firebase-messaging",
PluginVersion.PlayServicesVersionConstraint);
}
}
PluginVersion.cs
public class PluginVersion
{
public const string PlayServicesVersionConstraint = "9.6+";
}
After the ResolveClientJars command, all dependencies are copy into the Plugins/Android folder.
AndroidManifest.xml
firebase-analytics-9.6.1
firebase-analytics-impl-9.6.1
firebase-common-9.6.1
firebase-core-9.6.1
firebase-iid-9.6.1
firebase-messaging-9.6.1
google-services.json
play-services-basement-9.6.1
play-services-tasks-9.6.1
support-annotations-24.0.0.jar
support-v4-24.0.0
Everything seems to work perfectly at this point and there are no error during the build process.
What I'm doing wrong?
You could use Firebase Push Notifications without creation additional plugin. But the flow is a little bit tricky. Here are quick steps:
In the Unity Editor. In the Build Settings select Google Android
Project flag and Export your project. Make sure that your bundle id
correspond to the bundle id at the Firebase Console.
In the Android Studio. Import your Unity project. IMPORTANT: Don't
Open, Import!
Setup .gradle scripts and add google-services.json file as described here.
Build project from Android Studio.
My app has several flavors for several markets in-app-billing systems.
I have a single library which shares the base code for all of my projects. So I decided to add those payment systems to this library as product flavors.
The question is can android library have product flavors?
If so, how can I include different flavors in respective flavor of the app?
I searched a lot, and I couldn't find anything about this scenario. The only close thing I found was this in http://tools.android.com/tech-docs/new-build-system/user-guide:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}
I changed configuration to different things but it did not work!
I'm using android studio 0.8.2.
Finally I found out how to do this, I will explain it here for others facing same problem:
If App and Library have same Flavor name(s)
It's possible since Gradle Plugin 3.0.0 (and later) to do something like:
Library build.gradle:
apply plugin: 'com.android.library'
// Change below's relative-path
// (as the `../` part is based on my project structure,
// and may not work for your project).
apply from: '../my-flavors.gradle'
dependencies {
// ...
}
android {
// ...
}
Project build.gradle:
buildscript {
// ...
}
apply plugin: 'com.android.application'
// Note that below can be put after `dependencies`
// (I just like to have all apply beside each other).
apply from: './my-flavors.gradle'
dependencies {
api project(':lib')
}
android {
productFlavors {
// Optionally, configure each flavor.
market1 {
applicationIdSuffix '.my-market1-id'
}
market2 {
applicationIdSuffix '.my-market2-id'
}
}
}
My flavors .gradle:
android {
flavorDimensions 'my-dimension'
productFlavors {
market1 {
dimension 'my-dimension'
}
market2 {
dimension 'my-dimension'
}
}
}
If App or Library has different Flavor-name (old answer)
The key part is to set publishNonDefault to true in library build.gradle, Then you must define dependencies as suggested by user guide.
Update 2022; publishNonDefault is now by default true, and setting it to false is ignored, since said option is deprecated.
The whole project would be like this:
Library build.gradle:
apply plugin: 'com.android.library'
android {
....
publishNonDefault true
productFlavors {
market1 {}
market2 {}
market3 {}
}
}
project build.gradle:
apply plugin: 'com.android.application'
android {
....
productFlavors {
market1 {}
market2 {}
market3 {}
}
}
dependencies {
....
market1Compile project(path: ':lib', configuration: 'market1Release')
market2Compile project(path: ':lib', configuration: 'market2Release')
// Or with debug-build type support.
android.buildTypes.each { type ->
market3Compile project(path: ':lib', configuration: "market3${type.name}")
}
}
Now you can select the app flavor and Build Variants panel and the library will be selected accordingly and all build and run will be done based on the selected flavor.
If you have multiple app module based on the library Android Studio will complain about Variant selection conflict, It's ok, just ignore it.
There are one problem with Ali answer. We are losing one very important dimension in our build variants. If we want to have all options (in my example below 4 (2 x 2)) we just have to add custom configurations in main module build.gradle file to be able to use all multi-flavor multi-buildType in Build Variants. We also have to set publishNonDefault true in the library module build.gradle file.
Example solution:
Lib build.gradle
android {
publishNonDefault true
buildTypes {
release {
}
debug {
}
}
productFlavors {
free {
}
paid {
}
}
}
App build.gradle
android {
buildTypes {
debug {
}
release {
}
}
productFlavors {
free {
}
paid {
}
}
}
configurations {
freeDebugCompile
paidDebugCompile
freeReleaseCompile
paidReleaseCompile
}
dependencies {
freeDebugCompile project(path: ':lib', configuration: 'freeDebug')
paidDebugCompile project(path: ':lib', configuration: 'paidDebug')
freeReleaseCompile project(path: ':lib', configuration: 'freeRelease')
paidReleaseCompile project(path: ':lib', configuration: 'paidRelease')
}
Update for Android Plugin 3.0.0 and higher
According to the official Android Documentation - Migrate dependency configurations for local modules,
With variant-aware dependency resolution, you no longer need to use variant-specific configurations, such as freeDebugImplementation, for local module dependencies—the plugin takes care of this for you
You should instead configure your dependencies as follows:
dependencies {
// This is the old method and no longer works for local
// library modules:
// debugImplementation project(path: ':library', configuration: 'debug')
// releaseImplementation project(path: ':library', configuration: 'release')
// Instead, simply use the following to take advantage of
// variant-aware dependency resolution. You can learn more about
// the 'implementation' configuration in the section about
// new dependency configurations.
implementation project(':library')
// You can, however, keep using variant-specific configurations when
// targeting external dependencies. The following line adds 'app-magic'
// as a dependency to only the "debug" version of your module.
debugImplementation 'com.example.android:app-magic:12.3'
}
So in Ali's answer, change
dependencies {
....
market1Compile project(path: ':lib', configuration: 'market1Release')
market2Compile project(path: ':lib', configuration: 'market2Release')
}
to
implementation project(':lib')
And plugin will take care of variant specific configurations automatically. Hope it helps to others upgrading Android Studio Plugin to 3.0.0 and higher.
My Android Plugin is 3.4.0,and I find that it doesn't need configurations now.All you need is to make sure the flavorDimensions and productFlavors in application contains one productFlavor of the same flavorDimensions and productFlavors in libraries.For sample:
In mylibrary's build.gradle
apply plugin: 'com.android.library'
android {
....
flavorDimensions "mylibFlavor"
productFlavors {
market1
market2
}
}
application's build.gradle:
apply plugin: 'com.android.application'
android {
....
flavorDimensions "mylibFlavor", "appFlavor"
productFlavors {
market1 {
dimension "mylibFlavor"
}
market2 {
dimension "mylibFlavor"
}
common1 {
dimension "appFlavor"
}
common2 {
dimension "appFlavor"
}
}
}
dependencies {
....
implementation project(path: ':mylibrary')
}
After sync,you can switch all options in Build Variants Window:
To get the flavors working on an AAR library, you need to define defaultPublishConfig in the build.gradle file of your Android Library module.
For more information, see: Library Publication.
Library Publication
By default a library only publishes its release variant. This variant
will be used by all projects referencing the library, no matter which
variant they build themselves. This is a temporary limitation due to
Gradle limitations that we are working towards removing. You can
control which variant gets published:
android {
defaultPublishConfig "debug" }
Note that this publishing configuration name references the full
variant name. Release and debug are only applicable when there are no
flavors. If you wanted to change the default published variant while
using flavors, you would write:
android {
defaultPublishConfig "flavor1Debug" }
I also ran into a problem compiling modules for various options.
What i've found:
It looks like we don't need add publishNonDefault true into lib's build.gradle file, since Gradle 3.0.1.
After decompiling a class BaseExtension found this:
public void setPublishNonDefault(boolean publishNonDefault) {
this.logger.warn("publishNonDefault is deprecated and has no effect anymore. All variants are now published.");
}
And instead of:
dependencies {
...
Compile project(path: ':lib', configuration: 'config1Debug')
}
We should use:
dependencies {
...
implementation project(':lib')
}
Only the important thing, is to add a configurations {...} part to the build.gradle.
So, the final variant of app's build.gradle file is:
buildTypes {
debug {
...
}
release {
...
}
}
flavorDimensions "productType", "serverType"
productFlavors {
Free {
dimension "productType"
...
}
Paid {
dimension "productType"
...
}
Test {
dimension "serverType"
...
}
Prod {
dimension "serverType"
...
}
}
configurations {
FreeTestDebug
FreeTestRelease
FreeProdDebug
FreeProdRelease
PaidTestDebug
PaidTestRelease
PaidProdDebug
PaidProdRelease
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':lib')
...
}
Also, you can use Filter variants to restrict build variants.
P.s. don't forget to include modules in the settings.gradle file, like:
include ':app'
include ':lib'
project(':lib').projectDir = new File('app/libs/lib')
At the moment it's not possible, although if I recall correctly its a feature they want to add. (Edit 2: link, link2 )
Edit:
For the moment I'm using the defaultPublishConfig option to declare which library variant get's published:
android {
defaultPublishConfig fullRelease
defaultPublishConfig demoRelease
}
I know this subject has been closed, but just an update with gradle 3.0, see this : https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#variant_aware and grep matchingFallbacks and missingDimensionStrategy.
Now it's way more simple to declare the dependencies between module flavors.
...and in this precise case with gradle3.0, as flavors share the same name, gradle would map them magically, there is no configuration required.
In this situation. How could I import the dependency for a specific build. For example: market1Common1Debug
market1Common1DebugImplementation 'androidx.appcompat:1.2.0'
Just when I'd achieved an effective development and build environment with the android-maven-plugin, the new kid on the block, Gradle, starts making inroads into the Android circles. Not being hot on Groovy and with the android-gradle plugin almost as fragmented as the OS itself I've hit some issues. Specifically around building library projects, with flavours and our buddy Robolectric.
Short version
I am at a loss as to what my next move should be upon encountering the gradle error;
Cannot add a SourceSet with name 'testDebug' as a SourceSet with that name already exists.
The error emanates from having productFlavours on a library (i.e. moving to the 0.9.2 android build system) and the gradle-android-test-plugin recently forked by the team over at Robolectric from Jake's creation (see here). I have followed all lines of investigation to near exhaustion and can report that the meer existence of the 'android-test' plugin within my library gradle file sends things awry.
Longer version
Here is the abridged application build.gradle file with pertinent information retained;
apply plugin: 'android-library'
apply plugin: 'android-test'
...
android {
compileSdkVersion 19
buildToolsVersion '19.0.3'
defaultConfig {
minSdkVersion 18
targetSdkVersion 19
versionCode buildNumber().toInteger()
versionName "1.0.0"
}
productFlavors {
estimote {
dependencies {
compile '<flavour specific dependency>'
}
}
radius {
dependencies {
compile '<flavour specific dependency>'
}
}
}
}
...
dependencies {
compile 'com.squareup.dagger:dagger:1.2.1'
compile 'com.squareup.dagger:dagger-compiler:1.2.1'
compile 'com.google.code.gson:gson:2.2.+'
// Testing dependencies
androidTestCompile 'org.mockito:mockito-core:1.9.5'
androidTestCompile 'com.squareup:fest-android:1.0.7'
androidTestCompile 'org.hamcrest:hamcrest-all:1.3'
androidTestCompile 'org.robolectric:robolectric:2.2'
androidTestCompile 'junit:junit:4.11'
}
And here is the abridged root build.gradle file.
buildscript {
repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:0.9.2'
classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.9.+'
}
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
}
}
If you've got this far down the page, give yourself a pat on the back. Now, the eagle eyed amongst you have probably noticed the omission on the sourceSets redirection with commands akin to;
sourceSets {
androidTest {
setRoot('src/test')
}
}
After the initial error is corrected these lines will need to be reinstated to inform gradle of the project's structure. The project's structure is standard and looks like;
- project_name
+ gradle
- lib
+ flavour1
+ flavour2
- main
+ java
- test
+ java
build.gradle
build.gradle
gradle.properties
settings.gradle
What is being used
The app is using gradle-1.10-all, 0.9.2 android-gradle plugin and 0.9.+ gradle-android-test-plugin.
The question
How should the project be set-up/changed to facilitate Robolectric testing on a library with flavours? Is this even possible yet?
I ran into the same issue, dug into the code, fixed it, and submitted a pull request which has just now been merged. See my explanation on the PR for details, but it boils down to a bad optimization in the plugin code:
https://github.com/robolectric/robolectric-gradle-plugin/pull/70
As of today you need to clone the repo and build and install the plugin to your local maven repo. The next time they do a release to maven central (perhaps release 0.13.1?), you'll be able to use the plugin directly from there.
I have been using this tutorial to educate myself on how to build APK outside Eclipse by just using command line (and Ant) - http://www.androidengineer.com/2010/06/using-ant-to-automate-building-android.html
Now that build system will be shifting toward Gradle I would like to have similar advanced tutorial for reference. Most of the tutorials out there (like this one)deal just with basic stuff but I would like to know how to perform some "advanced" things like automatically replacing values in code during build (so that I can have multiple variants of APK).
Standard examples provided by Google are here
http://tools.android.com/tech-docs/new-build-system/gradle-samples-0.4.2.zip?attredirects=0&d=1
For automatically changing values in code use BuildConfig class. Examples are in the link above.
Variants are explained here http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
UPDATE
as this example gets bit stale here is pasetbin to newer version http://pastebin.com/FmcCZwA5
main difference is Robolectric support provided by plugin, and support library fetched from SDK internal repo
Older version
Less basic example with Robolectric and AndroidAnnotations
Use nexus
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.4'
}
}
apply plugin: 'android'
repositories {
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
use AndroidAnnotation processor, Robolectric local tests and Jackson
configurations {
compile
testLocalCompile.extendsFrom(compile)
androidannotations.extendsFrom(compile)
}
dependencies {
compile files('libs/android-support-v4.jar')
compile 'org.androidannotations:androidannotations-api:3.0-SNAPSHOT'
compile 'com.github.japgolly.android:svg-android:2.0.3'
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.12'
testLocalCompile 'junit:junit:4.8.2'
testLocalCompile 'org.robolectric:robolectric:2.2-SNAPSHOT'
testLocalCompile 'com.google.android:android:4.0.1.2'
testLocalCompile 'com.google.android:support-v4:r6'
testLocalCompile 'org.roboguice:roboguice:2.0'
androidannotations 'org.androidannotations:androidannotations:3.0-SNAPSHOT'
}
android {
compileSdkVersion 17
buildToolsVersion "17.0.0"
Configure standard instrumentation tests
defaultConfig {
minSdkVersion 7
targetSdkVersion 16
testPackageName "com.mypackage.myapp.test"
testInstrumentationRunner "com.maypackage.myapp.test.Runner"
}
}
Invoke AndroidAnnotations processor on all variants
afterEvaluate { project ->
android.applicationVariants.each { variant ->
variant.javaCompile.options.compilerArgs += [
'-classpath', configurations.compile.asPath,
'-processorpath', configurations.androidannotations.asPath,
'-processor', 'org.androidannotations.AndroidAnnotationProcessor',
'-AandroidManifestFile=' + variant.processResources.manifestFile
]
}
}
Define sourcesets for Robolectric local tests
sourceSets {
testLocal {
java.srcDir file('src/test/java')
resources.srcDir file('src/test/resources')
}
}
Local Robolectric tests task
task localTest(type: Test, dependsOn: assemble) {
testClassesDir = sourceSets.testLocal.output.classesDir
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split('/')
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
sourceSets.testLocal.compileClasspath += files(buildDir)
sourceSets.testLocal.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.testLocal.runtimeClasspath
}
Run Robolectric in debug mode
localTest.doFirst {
jvmArgs '-Xdebug',
'-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005'
}