Search code examples
androidbuildandroid-gradle-pluginbuild.gradleandroid-productflavors

How to access the class of product flavor from main directory?


There are two product flavor in my application i-e
flavorOne(src/flavorOne/java) and flavorTwo(src/flavorTwo/java). Main (src/main/java) is directory form where both flavor are using the classes. I want to start activity src/flavorTwo/java/ActivityB.java from the Activity present in src/main/java/ActivityA. while running the flavorTwo it works but when i switch the flavorOne it shows the import com.packagename.ActivityB error.

+ App // module
    |- src
       |- main// shared srcDir
          |- java
           |- SharedActivity
       + flavorOne
          |- java
           |- FlavorOneActivity
       + flavorTwo
          |- java
           |- FlavorTwoActivity

Here is SharedActivity.java in dir src/main/java/SharedActivity.java

package com.example.buildvariants;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
/**********************  works if it is in flavorOne otherwise it shows error on this package import ***********/
import com.example.buildvariants.flavorOne.LoginActivity;

public class SharedActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        //if flavor is flavorTwo hide the fab
        //else flavor is flavorOne show fab and launch activity under flavorOne/java/LoginActivity.java
        if (BuildConfig.FLAVOR.equalsIgnoreCase("flavorTwo")) {
            fab.setVisibility(View.GONE);
        } else {
            fab.setVisibility(View.VISIBLE);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                    startActivity(new Intent(SharedActivity.this, LoginActivity.class));

                }
            });
        }
    }
}

Activity under flavorOne src/flavorOne/FlavorOneMainActivity.java

package com.example.buildvariants.flavorOne;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.example.buildvariants.R;
import com.example.buildvariants.SharedActivity;

public class FlavorOneMainActivity extends AppCompatActivity {

    private static final String TAG =FlavorOneMainActivity.class.getSimpleName() ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flavor_one_activity_main);
        Intent intent = new Intent(FlavorOneMainActivity.this, SharedActivity.class);
        startActivity(intent);
    }
}

Activity under flavorTwo src/flavorOne/FlavorTwoMainActivity.java

package com.example.buildvariants.flavorTwo;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.example.buildvariants.R;
import com.example.buildvariants.SharedActivity;

public class FlavorTwoMainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flavor_two_activity_main);
        startActivity(new Intent(FlavorTwoMainActivity.this, SharedActivity.class));
    }
}

Shows error on package import of SharedActivity(src/main/java/) as listed below when i changed the build variants flavorTwo.

package com.example.buildvariants;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
/********error  on package import***********/
import com.example.buildvariants.flavorOne.LoginActivity;

public class SharedActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        //if flavor is flavorTwo hide the fab
        //else flavor is flavorOne show fab and launch activity under flavorOne/java/LoginActivity.java
        if (BuildConfig.FLAVOR.equalsIgnoreCase("flavorTwo")) {
            fab.setVisibility(View.GONE);
        } else {
            fab.setVisibility(View.VISIBLE);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                    startActivity(new Intent(SharedActivity.this, LoginActivity.class));

                }
            });
        }
    }
}

What would be the best solution for this problem?


Solution

  • It's failing because when your building your app only one product flavor's code exists at a time. So what you really want to do is have a single Class name used in two product flavors.

    Imagine we have a single class we want to be replaced per product flavor, lets call it ReplacableActivity.java

    For the replacement to work both product flavors need to have this class and main's source set will not have the class

    example:

    src/main/com/blah/myApp/ReplacableActivity #<- should not exist
    # exists and is the implementation of ReplacableActivity for `flavorOne`
    src/flavorOne/com/blah/myApp/ReplacableActivity.java
    # exists and is the implementation of ReplaceableActivity.java for `flavorTwo`
    src/flavorTwo/com/blah/myApp/ReplacableActivity.java
    

    Now for all product flavors your building the ReplacableActivity exists and can be referenced from the main source set. At build time only the ReplaceableActivity for that specific flavor gets packaged with the app. And now your import will work as expected import com.blah.myApp.ReplaceableActivity; from the main source set.

    Edit:

    If your only concern is hiding or showing a single element then the above is overkill. It would be much easier to get it from the BuildConfigField

    android {
        productFlavors {
            flavorOne {
                buildConfigField "boolean", "flavorShowsFab", 'false'
            }
            flavorTwo {
                buildConfigField 'boolean', 'flavorShowsFab', 'true'
            }
        }
    

    Then in your java code just do

    findViewById(R.id.myHidableFab).setVisibility(BuildConfig.flavorShowsFab ? View.VISIBLE : View.GONE));