Adding Android AppWidget just for one Product Flavor

642 views Asked by At

I have two gradle product flavors: for prod and for stage server.

productFlavors {
        stage {
            // stage specific stuff
        }
        production {
            // production only
        }
    }

Also I have an AppWidget must be able only for stage now. I think I should hide my AppWidgetProvider in manifest:

<receiver
    android:name=".appwidget.AppWidgetProvider"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
       android:name="android.appwidget.provider"
       android:resource="@xml/appwidget_info" />
</receiver>

But how can I do it just for prod product flavor? Or is there another way here?

3

There are 3 answers

0
thekevshow On BEST ANSWER

So we will have our example packages is as follows for your production and staging builds within the gradle product flavors.

productFlavors {
        production {
            packageName "x(path).x(path).gradleexample"
        }

        staging {
            packageName "x(path).x(path).gradleexample.staging"
        }
    }

Here is an example of your folder structure some of which may or may not apply to your project but should give you a run down of what is needed.

├── main
│   ├── AndroidManifest.xml
│   ├── ic_launcher-web.png
│   ├── java
│   │   └── x(path)
│   │       └── x(path)
│   │           └── gradlebuildexample
│   │               └── MainActivity.java
│   └── res
│       ├── drawable-hdpi
│       │   └── ic_launcher.png
│       ├── drawable-mdpi
│       │   └── ic_launcher.png
│       ├── drawable-xhdpi
│       │   └── ic_launcher.png
│       ├── drawable-xxhdpi
│       │   └── ic_launcher.png
│       ├── layout
│       │   └── activity_main.xml
│       ├── menu
│       │   └── main.xml
│       ├── values
│       │   ├── dimens.xml
│       │   ├── strings.xml
│       │   └── styles.xml
│       ├── values-v11
│       │   └── styles.xml
│       └── values-v14
│           └── styles.xml
├── production
│   └── java
│       └── x(path)
│           └── x(path)
│               └── gradlebuildexample
│                   └── Constants.java
└── staging
    ├── java
    │   └── x(path)
    │       └── x(path)
    │           └── gradlebuildexample
    │               └── Constants.java
    └── res
        ├── drawable-hdpi
        │   └── ic_launcher.png
        ├── drawable-mdpi
        │   └── ic_launcher.png
        ├── drawable-xhdpi
        │   └── ic_launcher.png
        ├── drawable-xxhdpi
        │   └── ic_launcher.png
        └── values
            └── string.xml

Now to be clear your string result set xml in the production and staging, among the values -> string.xml, will have to contain the following value appropriate for the manifest which will be described below.

So something like as follows

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="receiver">false</string>
</resources>

With my current understanding of your manifest it would look something like this

<receiver
    android:name=".appwidget.AppWidgetProvider"
    android:label="@string/app_name"
    android:enabled="@string/receiver">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data
       android:name="android.appwidget.provider"
       android:resource="@xml/appwidget_info" />
</receiver>

So this would work, but if you saw from my third comment, a much better and simpler solution would be to have a different manifest for each build phase, because you may want more changes than just one. So below I will provide that work around as well which you can extrapolate on what I have provided.

Using source set in gradle you can specify a different build path for each manifest as follows

sourceSets {

        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            res.srcDirs = ['src/main/res']
        }

        debug {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            res.srcDirs = ['src/main/debug/res']
        }

        release {
            manifest.srcFile 'src/main/release/AndroidManifest.xml'
            res.srcDirs = ['src/main/release/res']
        }
}
0
anil On

Ok, I tried all approaches and they all work:) I award @kev2316 with the bounty because all the ways base on his ideas.

And for future readers I want to leave some comments how I solve the problem in my project.

  1. let the AndroidManifest.xml in app/src/main stay immutable
  2. create AndroidManifest.xml in app/src/stage with this content.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="my.package" >
<application>
<!-- Declarations for the widget -->
    <receiver
        android:name=".appwidget.AppWidgetProvider"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/appwidget_info" />
    </receiver>
    <service
        android:name=".appwidget.AppWidgetRefreshService"
        android:permission="android.permission.BIND_REMOTEVIEWS"
        android:exported="false" />
</application>
</manifest>
  1. Profit! The result AndroidManifest.xml merges by gradle automatically. I have a custom Application class for the project, but it works perfect.

This approach is more common than turning off receiver with enabled option and allow to configure receiver and service as usual. Also I have to create just one additional file, so it's quite simple.

2
Simas On

Building on the other answer by @kev2316, here's a simpler way to accomplish that:

<meta-data
    android:name="android.appwidget.provider"
    android:resource="@xml"
    android:enabled="@bool/widget_enabled"/>

And in your build.gradle:

productFlavors {
    production {
        resValue "bool", "widget_enabled", "true"
    }
    staging {
        resValue "bool", "widget_enabled", "false"
    }
}