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

2

5

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?

Heathendom answered 2/12, 2015 at 7:18 Comment(6)
when u want to access a class that outside package , u have to import that package,for example, did u import flavourTwo inside falvorOne/java?Balsa
@user3624028 can you please post an example how to import flavourTwo inside falvorOne/java?Heathendom
for trail sake,try: import flavorOne.java;Balsa
@user3624028 i have imported that class but when i change build variants to another flavor shows error on that import.Heathendom
please add ur code snippetsBalsa
@user3624028 i have updated code snippets.Heathendom
A
7

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));
Alberich answered 2/12, 2015 at 9:32 Comment(4)
In my case both flavor has same UI but difference is one flavor contains floating action button and another doesn't. I want to launch activity from that floating action button in case of SecondFlavor. I have hide the floating action button by checking build variants in first flavor.Heathendom
Updated answer to show a simpler way to hide view based on productFlavorsAlberich
thanks for the solution, Is it only way to do this? In my case i have kept ReplacableActivity to main because it needs both flavor,and same code for both flavor. If the build is first flavor then i want to open activity present in first flavor on the action of of floating action button but not in case of Second Flavor. If the code is same for both except the actioin of FAB is it good to repeat the code in both flavor?Heathendom
Gradle is hyper flexible so you could implement this in many ways. That said, repeating code is never good. If you need code to be the same for both flavors except one decision point then using the BuildConfig value for an if (BuildConfig.blah) {} else {} makes the most sense... You don't want to maintain two copies of the same code. What if you find a bug/feature in duplicated code? Now you have to update two places instead of one.Alberich
C
1

Instead of creating different code for each flavor you can maintain same code by using inheritance concept and few changes in gradle and manifest files, we can achieve this in three steps

1) merge the source sets for each flavor in app level build.gradle file

sourceSets {
         flavorOne {
            java.srcDirs = ['src/main/java','src/flavorOne/java']
        }
    flavorTwo {
            java.srcDirs = ['src/main/java','src/flavorTwo/java']
        }
    }

2) the main trick happens here, create manifest for each flavor with launcher activity defined for them but not in main flavor

In main manifest file:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapplication">

        <application

    //no launcher defined here

        </application>

    </manifest>

In flavour one mainifest file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.myapplication">

    <application>
        <activity
            android:name="{Your launcher activity}">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

In flavour two manifest file:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.myapplication">

        <application>
            <activity
                android:name="{Your launcher activity}">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>

    </manifest>

so that we don't get error while merging manifest file while building gradle

3) So in main java src, create BaseSharedActivity.java and write your normal implementation, create SharedActivity.java in each flavor and call them from the launcher activity defined in each flavor.

In SharedActivity.java of each flavour:

public class SharedActivity extends BaseSharedActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
//You can override whatever method you want from BaseSharedActivity here 
}

This way you are not adding same code for SharedActivity and can give maximum customization for the same screen in different flavors, you can use the same logic for the launcher activity as well

Cluny answered 12/4, 2020 at 2:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.