3

I have an app that has 3 different flavors, full, part1 and part2.

All different flavors have different package names, so I can ship them as different apps.

Now I want that only part1 gets a menu item called Reload. The other 2 flavors shouldn't have this menu item. Is this possible?

I tried the following with the menu resources:

app
|
+-src
  |
  +-full
  |
  +-main
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |
  +-part1
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |
  +-part2

Where main_activity.xml for part1 is:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_reload"
        android:icon="@drawable/ic_reload"
        android:title="@string/action_reload"
        app:showAsAction="always"/>
</menu>

And main_activity.xml for main is:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>

However, if I build the app in any other build variant than part1, I get a compilation error in my MainActivity where I need to react to the menu selection:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_reload: // Compile error: This item is not available
            // TODO reload
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

It's quite obvious why that is. But do you have any suggestion what the solution is to customize menus for different build flavors?

Terry
  • 12,591
  • 12
  • 51
  • 84
  • 1
    you could have a BaseActivity with the common code and different MainActivity per flavor, where you supply a different version of `onOptionsItemSelected` – Blackbelt Apr 12 '16 at 09:31

5 Answers5

12

There's another way - create value file with boolean resource, with different value for each flavor e.g.:

main/res/values/bool.xml :

 <resources>
        <bool name="show_reload">false</bool>
    </resources>

part1/res/values/bool.xml :

    <?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="show_reload">true</bool>
</resources>

and then in your menu reource set the visibility value dependent on resource:

<menu ..>
    <item ..
      android:visible="@bool/show_reload"
      ..
    />
</menu>
sashk0
  • 801
  • 10
  • 10
10

If You don't want to copy whole class file, but to detect flavour or setting for any flavour and adjust it then do this:

In gradle file create config field:

defaultConfig {
    ...
    buildConfigField "boolean", "SHOW_MY_MENU_ITEM", "true"
}
productFlavors {
    FooFlavour {
        ...
        buildConfigField "boolean", "SHOW_MY_MENU_ITEM", "false"
    }
}

then build gradle. You can access this config field in Activity like this:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.foo_menu, menu);
    if (!BuildConfig.SHOW_MY_MENU_ITEM) {
        MenuItem myItem = menu.findItem(R.id.my_menu_item);
        myItem.setVisible(false);
    }
    return super.onCreateOptionsMenu(menu);
}

Similar, but in my opinion better approach is by using resValue, first create it in gradle file:

defaultConfig {
    ...
    resValue "bool", "show_my_menu_item", "true"
}
productFlavours {
    FooFlavour {
        ...
        resValue "bool", "show_my_menu_item", "false"
    }
}

After this resValue can be accessed directly in menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        ...
        android:visible="@bool/show_my_menu_item" />
</menu>
Darek Deoniziak
  • 682
  • 7
  • 16
  • 1
    In my humble opinion this is a far better answer. Much easier to create all your menu options and then remove the ones you don't need in your product flavor, than to have different versions of MainActivity. – SimonH Feb 22 '17 at 20:14
  • 1
    Updated answer with different approach, using resValue this time. – Darek Deoniziak Jul 07 '19 at 09:24
2

You can also create another xml file in the menu folder and create the same resource id inside it, for example:

app
|
+-src
  |
  +-full
  |
  +-main
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |     +-dummy_menus.xml

And then in the dummy menu create an item with the same id. You just won't use it because it will never be selected, as it never was inflated.

Omaraf
  • 793
  • 7
  • 15
  • 1
    ... this isn't a bad answer either. Both answers I've commented on are better than the accepted answer – SimonH Feb 22 '17 at 20:15
  • I've recently read another answer that might solve the same problem: You can add a ids.xml file in the values folder, where you can declare just the id, and as I've stated before, never really use it. – Omaraf Feb 23 '17 at 00:38
1

Create a MainActivity in the main source folder where you handle the normal common code. Create another MainActivity in the part1 source folder where you override onOptionsItemSelected where it's not a problem to have references to R.id.action_reload. That should work.

Joao Sousa
  • 3,731
  • 1
  • 22
  • 25
  • 1
    It is not a good solution since you'll end with to `MainActivity` classes, proper and elegant solution was posted by @sashk0 – Tomek Gozdek Oct 22 '18 at 15:22
0

You can define the content of the onOptionsItemSelected(MenuItem item) method in a helper and then using flavors load the needed helper :

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    HelperPart.selectItem(this, item);
}


// Helper loaded for flavor "part1"
static class MenuHelper{ 
    public static boolean selectItem(Activity act, MenuItem item){
        switch (item.getItemId()) {
            case R.id.action_reload: 
                // TODO reload
                return true;
            default:
                return act.onOptionsItemSelected(item);
        }
    }
}

// Helper loaded for flavor "part2" and "full"
static class MenuHelper{
    public static boolean selectItem(Activity act, MenuItem item){
        // Do nothing
    }
}
Guillaume Barré
  • 3,915
  • 2
  • 21
  • 46