Android Auto Setup with Gradle Build Flavors - android

I have a project where I am attempting to add Android Auto support. I have added the following code to my manifest as shown in the Auto documentation:
<application
....
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
....
<service
android:name="com.me.auto.MyMediaBrowserService"
android:exported="false">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
....
</applicaiton>
I'm also using different build flavors, defined in my gradle.build file:
defaultConfig {
applicationId "com.me"
minSdkVersion 16
//noinspection OldTargetApi
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
productFlavors {
regular {
applicationId "com.me"
}
different {
applicationId "com.meother"
}
}
When I build and install using the 'regular' flavor, android auto does not work. However, when I build and install using the 'different' flavor, everything works great. If I then change the regular applicaitonId to something else like 'com.menew', again Auto works great.
How is the applicationId in the build flavor making or breaking Android Auto functionality?

I am not absolutely sure, but I would guess this is related with the application id, e.g. you can make avoid full qualified package names by using the relative names you can use it in the manifest all all places. Check this:
<service
android:name="com.me.auto.MyMediaBrowserService" ...>
vs.
<service
android:name=".auto.MyMediaBrowserService" ...>
Make also sure that you have no hard coded packages in your code always use BuildCondig.APPLICATION_ID when you need your package name.

Looks like you have it mostly right. I would recommend these changes (based on https://developer.android.com/training/auto/audio/index.html) and see if this fixes it.
1) Remove the package name so it's not locked into one flavor. Alternatively, you can use ${applicationId} and gradle will insert the correct one.
2) Set the service to be exported (android:exported=true).
<application
....
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
....
<service
android:name="${applicationId}.auto.MyMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
....
</applicaiton>

Did you try to create a flavorDimensions?
You can try this.
flavorDimensions "mode"
productFlavors {
regular {
dimension = "mode"
}
different {
dimension = "mode"
}
}
if you want to get the version of your application
if (BuildConfig.Flavor.contains("regular") || BuildConfig.Flavor.contains("different")) {
// Your code goes here.
}
Hope this will help.

Related

How to detect when android wear device gets disconnected?

I had an app that could detect when an android wear device disconnected by using a WearableListenerService and onPeerConnected/onPeerDisconnected.
It seems these have been deprecated, so I am now trying to use onCapabilityChanged, but I cannot get this function called. I use this in my manifest for my service. Documentation on these features are not very good.
<intent-filter>
<action android:name="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
</intent-filter>
So I finally got it to work. It took a combination of things that needed to be set up, but I'll list them all.
The Gradle. You need to make sure the mobile version and the wearable version has the same application id, the same version code, the same version name, and possibly the same play-services version. This is easier to handle if you use the project gradle file to hold these values and have each module reference these values.
In the Root build.gradle file have:
ext {
TARGET_SDK_VERSION = 25
VERSION_CODE = 7
VERSION_NAME = '2.0'
COMPILE_SDK_VERSION = 25
BUILD_TOOLS_VERSION = '25.0.2'
APPLICATION_ID = "com.example.projectname"
PLAY_SERVICES_WEARABLE = 'com.google.android.gms:play-services-wearable:9.4.0'
}
In each of the module build.gradle files, these can be referenced as shown below:
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.COMPILE_SDK_VERSION
buildToolsVersion rootProject.ext.BUILD_TOOLS_VERSION
defaultConfig {
applicationId rootProject.ext.APPLICATION_ID
minSdkVersion 20
targetSdkVersion rootProject.ext.TARGET_SDK_VERSION
versionCode rootProject.ext.VERSION_CODE
versionName rootProject.ext.VERSION_NAME
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'com.google.android.wearable:wearable:2.0.1'
compile 'com.google.android.support:wearable:2.0.1'
compile rootProject.ext.PLAY_SERVICES_WEARABLE
}
The Manifest. With the new updates to the play services, a WearableListenerService must now have an intent-filter defined for each overrided function to be called by the android system. In the case of the onCapabilityChanged function, the intent filter should be defined as:
<service
android:name=".MyWearableListenerService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
<data android:scheme="wear" android:host="*"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/PREF"/>
<data android:scheme="wear" android:host="*" android:pathPrefix="/start"/>
</intent-filter>
</service>
The intent-filter for onCapabilityChanged is com.google.android.gms.wearable.CAPABILITY_CHANGED. Along with that, the intent-filter also needs to be told the data scheme and host. This can simply be data android:scheme="wear" android:host="*". The pathPrefix can be omitted for this intent-filter. Notice that the intent-filter for the com.google.android.gms.wearable.DATA_CHANGED and com.google.android.gms.wearable.MESSAGE_RECEIVED needs the pathPrefix defined to be able to have their respective functions called in the service.
The capability file. In order for the onCapabilityChanged function to launch, the system needs to detect a device with a capability being connected. To do this, we must have the capability defined in an xml file in each module.
To do this, in each module, save a file named wear.xml in the res/values directory. The file must have a string array named android_wear_capabilities with items that describe the capabilities you wish your module to advertise to another device. Below is an example of a wear.xml file included in a wearable module.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="android_wear_capabilities">
<item>verify_remote_wear_app</item>
</string-array>
</resources>
First, It is important to note that the file must be named wear.xml and must be placed in the values directory. Secondly, the string-array must be named android_wear_capabilities. Also make sure that every capability in each module has a unique name.
If any of the above is not correct, then the onCapabilityChanged function will never be called, and you will be pulling your hair out in frustration.
Now, to actually tell if a device was disconnected, use the onCapabilityChanged function:
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
super.onCapabilityChanged(capabilityInfo);
if(capabilityInfo.getNodes().size() > 0){
Log.d(TAG, "Device Connected");
}else{
Log.d(TAG, "No Devices");
}
}
This function will tell you when a device has connected or disconnected, assuming only 1 device is connected at a time.

Multiple app icons being installed when using Flavors and multiple manifest files

I currently have a project that needs to contain two different forms of codebase, legacy and an updated version of the application. I am using Flavors for this, but am running into an issue where two app icons are being installed. The reason is because both the legacy codebase and the updated codebase have their own manifest.xml, and inside the manifest are declarations for identifying the Main launch class and their relative app icon.
<!-- legacy code manifest -->
<activity
android:name="legacy.activity.RegistrationActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/CustomAppTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- updated code manifest -->
<activity
android:name="updated.activity.RegistrationActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/CustomAppTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
How can I get around this? If I remove the intent-filter from the updated code base, which is the Main codebase, the Flavor will not install two icons. However, I am unable to run the Main codebase because I have not declared an entry point in my Manifest. And conversely, if I remove the intent-filter from the Flavor and keep it in my Main codebase, the Flavor code will not run. The two RegistrationActivity classes are different, just with the same name. The Legacy code does not really share much of the updated codebase. Any suggestions other than separate into different projects?
Some have requested my setup with Flavors in gradle, here is snapshot of it.
productFlavors {
standard {
applicationId 'updated.android.example'
manifestPlaceholders = [package_name: "updated.android.example", primary_lang: "en"]
signingConfig signingConfigs.keystore
}
legacyTest {
applicationId 'legacy.android.example.debug'
manifestPlaceholders = [package_name: "legacy.android.example.debug",
target : "Test", primary_lang: "en"]
signingConfig signingConfigs.keystore
}
legacyProd {
applicationId 'legacy.android.example.prod'
manifestPlaceholders = [package_name: "legacy.android.example.prod",
target : "Prod", primary_lang: "en"]
signingConfig signingConfigs.keystore
}
This line:
<category android:name="android.intent.category.LAUNCHER"/>
says that you have a launching activity. Set them to a default activity setting(this is just "another activity", create a third class that is the launcher-class. This class will automatically redirect to one of the two other activities based on whatever specifications you may have, such as API level or brand. Any specifications you have as to which to launch, set them and the user will not know there is a handling activity.
When you have two launcher activities, they show as two applications. This is probably because the system cannot determine what to redirect to automatically. So there are two depending on what to launch.
How can I get around this? If I remove the intent-filter from the updated code base, which is the Main codebase,
Don't remove it. Change it. E.g.:
<intent-filter>
<action android:name="com.yourpackage.name.CLASSNAME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
You need separate <activity android:name="legacy.activity.RegistrationActivity" and <activity android:name="updated.activity.RegistrationActivity" you two manifests, one for each flavor.
eg.:
src/main/AndroidManifest.xml (with everything except: *.activity.RegistrationActivity)
src/legacy/AndroidManifest.xml (with legacy.activity.RegistrationActivity)
src/updated/AndroidManifest.xml (with updated.activity.RegistrationActivity)

Replace launching activity with flavors

Is it possible to replace the activity that gets the intent-filter
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
by configuring flavors?
There are a number of ways to accomplish this thanks to the manifest merger.
The easiest way is to use a placeholder in your manifest and define the appropriate class in your build.gradle.
For example, in your manifest:
<activity android:name="${launchActivityName}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
And in your build.gradle:
productFlavors {
flavor1 {
manifestPlaceholders = [ launchActivityName:"com.example.MainActivity"]
}
}
You can also include a different manifest file with each flavor.
Yes, just add a manifest file in the folder of the product flavour and
The Manifest of the product flavor is merged on top of the Manifest of the main configuration.
More info here: http://tools.android.com/tech-docs/new-build-system/build-system-concepts
In addition to #Tanis answer, I want to add some parts you should aware of.
productFlavors {
lite {
dimension "default"
applicationId "com.foo.bar"
manifestPlaceholders = [ applicationLabel:"#string/app_name_foo"
, launcherAct: "com.foo.qux.activity.Hello"
, launcherAct2: "com.foo.qux.activity.World"]
}
full {
dimension "default"
applicationId "com.foo.qux"
manifestPlaceholders = [ applicationLabel:"#string/app_name_bar"
, launcherAct: ".activity.World"
, launcherAct2: ".activity.Hello"]
}
}
If let's say, your World.java exists in com.foo.qux.activity folder, then you should use full path com.foo.qux.activity.World instead of .activity.World when put it inside applicationId "com.foo.bar" flavor, or else it will auto prepend to com.foo.bar.activity.World which the file doesn't exist.
Unlike android:label, you can't use something like #string/launcher_class in android:name, or else you will get error: attribute 'android:name' in <activity> tag must be a valid Java class name. when build.
Ensures you don't duplicate the class, e.g. :
<activity
android:name="${launcherAct}"
android:label="${applicationLabel}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="${launcherAct2}"
android:label="${applicationLabel}">
</activity>
Don't forgot to change from .activity.World to android:name="${launcherAct2}" or else you end up duplicated activity (i.e. one in AndroidManifest.xml, the other one in manifestPlaceholders).
The Unresolved class red indicator can safely ignored, seems like Android Studio bug:
I make a bug report here.

Android Studio two flavors with different manifest files

I'm having issues with defining two different manifest files for my flavors in Android Studio. This is my current project structure:
The AndroidManifest.xml in the free flavor looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
</manifest>
The AndroidManifest.xml in the main flavor has no uses-permissions, but contains the rest of the manifest code that is shared between all flavors.
The AndroidManifest.xml in the pro flavor looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>
build.gradle defines the two flavors like
productFlavors {
free {
applicationId 'se.example.package.free'
minSdkVersion 14
targetSdkVersion 21
versionCode 1
versionName '1.0'
}
pro {
minSdkVersion 14
applicationId 'se.example.package.pro'
targetSdkVersion 21
versionCode 2
versionName '1.1'
}
}
The result that I am expecting is that the different flavors defines different uses-permissions. This is not the case. The result is currently that the both flavors only defines the <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> as defined in AndroidManifest.xml in the pro flavor.
I have tried:
Clean project
Rebuild project
Restart Android Studio
Sync gradle
But without success. How am I to fix this? Any help is appreciated.
EDIT 1
I changed the location of each flavors AndroidManifest.xml file from each of the res folders to free and pro folder. The result of this:
Pro flavor shows Licence permission as expected.
Free flavor shows permissions from both AndroidManifest.xml
files, License and network permissions (Should be only network)
This feels like an issue of project structure. What to make of this?
EDIT 2
I pulled the merge reports as Commonsware hinted, these are the reports regarding uses-permissions
Free:
uses-permission#com.android.vending.CHECK_LICENSE
ADDED from qwknoteGIT:licencing-library:unspecified:26:5
android:name
ADDED from qwknoteGIT:licencing-library:unspecified:26:22
Pro:
uses-permission#com.android.vending.CHECK_LICENSE
MERGED from qwknoteGIT:licencing-library:unspecified:26:5
Tech background:
on this link it explains the techniques and parameters that can be use for manifest merging: https://developer.android.com/studio/build/manage-manifests#merge_rule_markers
One in specific is the tools:node that points out how certain XML nodes on the manifest should behave whilst merging.
Solution:
to achieve some permisions in one and different in other manifest, add ALL permissions you need to the main and in the flavours manifest remove the ones you don't need, like the example below:
free remove the check license
<uses-permission
android:name="com.android.vending.CHECK_LICENSE"
tools:node="remove"/>
Your problem is coming from a library, not your flavors. Specifically, qwknoteGIT:licencing-library is requesting CHECK_LICENSE.
If you are not using that library in all flavors, use a flavored compile statement (e.g., proCompile) to only use that library in that flavor.
If you are using the library for all flavors, but feel confident that you do not need the permission in one flavor, that's where a tools:node attribute can be used, in the flavor's manifest, to block out that permission supplied by the library.
And the manifest merger report is your friend. :-)
This should solve the problem, at least. I find it useful in specifying the exact manifest to use for each variant. Cheers! It explicitly directs to the manifest file under each variant folder.
android {
productFlavors {
prod {
manifest.srcFile "prod/AndroidManifest.xml"
}
dev {
manifest.srcFile "dev/AndroidManifest.xml"
}
}
...
}
Specify your Manifest exclusively under sourceSets, In your App build.gradle
android {
productFlavors {
bizdartFlavourNoCallLog {
minSdkVersion 16
applicationIdSuffix '.bizdart'
targetSdkVersion 26
dimension "tier"
sourceSets {
main {
manifest.srcFile "src/bizdartFlavourNoCallLog/AndroidManifest.xml"
}
}
copy {
from 'src/bizdartFlavourNoCallLog/'
include '*.json'
into '.'
}
}
}
}
See https://developer.android.com/studio/build/manifest-merge with param tools:node="merge"
High priority manifest (Free):
<activity android:name="com.example.ActivityOne"
android:screenOrientation="portrait"
tools:node="merge">
</activity>
Low priority manifest (Main):
<activity android:name="com.example.ActivityOne"
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Merged manifest result:
<activity android:name="com.example.ActivityOne"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
You should change your code:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
for:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.your.appid">
I encountered the same problem, and I found that it was because I put "pro" and "tree" flavor folder under the lib project, and the problem was solved after I move the flavors folder under the app project

Product Variants: Multiple Activities

I'm using Product Variants in gradle/android studio to achieve the following project setup:
Two apps, that are 80% similar in one android studio project.
Each app should have its own manifest package path (they should basically behave like two independent apps - with it's own google api and push keys)
I've followed multiple tutorials in my attempt to achieve this (placeholders, multiple manifests) but nothing works.
Following this tutorial I did the following:
http://www.kevinrschultz.com/blog/2014/03/23/using-android-content-providers-with-multiple-package-names/
My build.gradle:
android {
compileSdkVersion 19
buildToolsVersion '20'
defaultConfig {
minSdkVersion 10
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
productFlavors {
app1 {
packageName "com.test.app1"
//applicationId "com.test.app1"
}
app2 {
packageName "com.test.app2"
//applicationId "com.test.app2"
}
}}
And here my manifest files.
src/main/manifest:
<?xml version="1.0" encoding="utf-8"?>
<application>
</application>
src/app1/manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.test.app1”
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.test.app1.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait" >
</activity>
</application>
</manifest>
src/app2/manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.test.app2”
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.test.app2.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait" >
</activity>
</application>
</manifest>
This gives me the following error message:
Manifest merger failed : Main AndroidManifest.xml at AndroidManifest.xml manifest:package attribute is not declared
Unfortunately I can't set a package to each individual manifest tag as my app requires different package paths. If I do it anyway, the merger is not able to merge the manifest files because of different package values. Besides packageName I also tried setting "applicationId" but this doesn't work, either. Using a placeholder like this package="${applicationId}" doesn't work because it doesn't resolve the variable value.
Any ideas how to solve this problem? I'm using Android Studio 0.8.2 with gradle 0.12.2
You can safely add
<manifest xmlns:android="..."
package="com.test.app">
</manifest>
in your main manifest.
But you have to use applicationId and not packageName in your build.gradle.
The applicationId of the build.gradle will overwrite this value during your different builds.
A better way of doing this would be to use applicationIdSuffix instead of applicationId for your different product flavors.
The field have to be present in your main manifest.
In addition to your src/app1/AndroidManifest.xml and src/app2/AndroidManifest.xml you will have a src/main/AndroidManifest.xml which can contain elements both apps share.
In your flavor-specific manifests, you do not have to specify a package attribute, since that is already defined in your build.gradle.
In your main manifest however, you need to define a package attribute, to specify the package name used for your shared resources:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.app">
</manifest>
Make sure that you do not have any dependent libraries, which is also using that name, or you will get another build error.
Furthermore, you will want to specify applicationId instead of packageName in you build.gradle to decouple the package name used for identification of your app from the package name used for internal resource access.
Check this link for further information: http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename
I resolved same issue putting the same package="com.ex.app" in every flavor's manifest and putting applicationId and useOldManifestMerger true into build.gradle file
android {
useOldManifestMerger true
productFlavors{
app1{
applicationId = "com.ex.app1"
}
app2{
applicationId = "com.ex.app2"
}
}
}
I started over fresh but this time Android Studio generated the Variant configuration for me (under project settings there's a tab for flavors, variants, etc.)
I also decided to change the package name of all three variants to the same name as the identification of each app (variant) is done by the applicationId (which has nothing to do with the actual package names of the variants).
Thanks to pdegand59 for his help!

Categories

Resources