1

EDIT#3--changing directory structures since advised wrong.

Based on this link I followed from a SO question, I need my file structure to appear like this in Android Studio (AS) 1.1.0 in order to get both a free and for-pay version of my GPS (Google Play Store) app:

+-- main
¦   +-- AndroidManifest.xml
¦   +-- java
¦   ¦   +-- com
¦   ¦       +-- whatever
¦   ¦           +-- kakurocombos
¦   ¦               +-- MyActivity.java 

¦   +-- res        
¦       +-- layout
¦       ¦   +-- activity_main.xml        
+-- FreeVersion
¦   +-- java
¦       +-- com
¦           +-- whatever
¦               +-- kakurocombos
¦                   +-- Free.java (where FREE = true;)
+-- Pro
    +-- java
    ¦   +-- com
    ¦       +-- whatever
    ¦           +-- kakurocombos
    ¦               +-- Free.java (where FREE = false;)
    +-- res
        +-- values
            +-- string.xml

EDIT-- All of the above structure must be under src as pointed out below in comments.

I chose that structure because this is how that link (above) shows its structure:

├── main
│   ├── AndroidManifest.xml
│   ├── ic_launcher-web.png
│   ├── java
│   │   └── be
│   │       └── tamere
│   │           └── gradlebuildtypesexample
│   │               └── 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
│       └── be
│           └── tamere
│               └── gradlebuildtypesexample
│                   └── Constants.java
└── staging
    ├── java
    │   └── be
    │       └── tamere
    │           └── gradlebuildtypesexample
    │               └── 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

Note the absence of res under production because it will use res under main.

Note the presence of res under staging since it uses different resources since it's the 2nd APK/package.

Here's how the directory structure looks in Windows 7 Explorer:

enter image description here

Here's how it looks in AS: (EDITED!)(TWICE)

enter image description here

Here's build.gradle:

apply plugin: 'com.android.application'

android
{
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig
    {
        applicationId "com.dslomer64.kakurocombosbuildvariants"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes
    {
        release
        {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors
    {
        paid
        {
            applicationId "com.dslomer64.kakurocombos.paid"
        }
        free
        {
            applicationId "com.dslomer.kakurocombos.free"
        }
    }
}

dependencies
{
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0'
}

The link alluded to at beginning of post has a few errors, including failure to append .paid to the first package in the productFlavors block.

DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • For starters you're using applicationId in your gradle instead of packageName like in the example – ElefantPhace May 03 '15 at 02:01
  • Here's why. When using packageName: Error:(28, 0) Gradle DSL **method not found: 'packageName()'** Possible causes:
    • The project 'KakuroCombosBuildVariants' may be using a version of Gradle that does not contain the method. **Open Gradle wrapper file**
    • The **build file may be missing a Gradle plugin.** Apply Gradle plugin
    • See edited original questoin for gradle wrapper file. Also, missing plugin leads me nowhere.\
    – DSlomer64 May 03 '15 at 02:04
  • 1
    @ElefantPhace-- In addition to my previous comment: Also, see this link http://stackoverflow.com/questions/18491649/how-to-change-the-android-app-package-name-when-assembling-with-gradle where applicationId is used in this context. This is one of several reasons I ask if the link is garbage. – DSlomer64 May 03 '15 at 02:12
  • That was the only thing I noticed at first. Didn't actually look into it. Thanks for clarifying – ElefantPhace May 03 '15 at 02:18
  • 1
    Good reference post: http://stackoverflow.com/questions/16737006/using-build-flavors-structuring-source-folders-and-build-gradle-correctly – stkent May 03 '15 at 02:35

2 Answers2

2

Everything related to your flavors should be inside the src folder, e.g.

src/main/...
src/free/...
src/pro/...

Right now your src folder is at the same levels as the flavor folders, which is incorrect.

See this documentation for confirmation of the above.

stkent
  • 19,772
  • 14
  • 85
  • 111
  • @stkent--So is the structure provided by the link just garbage? Or did I misinterpret it? I agree with you, which doesn't agree with the sample code and structure at the link. I'll make these changes. I guess this explains why `.MyActivity` can't be resolved. – DSlomer64 May 03 '15 at 02:19
  • @DSlomer64, the structure shown by the link you posted represents what is _inside_ the `src` folder. Notice that there are no folders named `src` in their hierarchy. The preceding paragraphs also begin (my emphasis): "**In the src folder**, we've created two directories whose names must match the flavors." – stkent May 03 '15 at 02:21
  • @stkent--thank you very much for your interest and patience. I will probably be on this through the night since I'm full of caffeine. I think I'll add sugar to the mix and pull an all nighter. I'm so mad at myself, iff'n I was a bull I'd snot. (Ya gotta be REALLY OLD to know that one.) – DSlomer64 May 03 '15 at 02:24
  • @stkent--what do I do about gradle telling me that .MyActivity is not an Activity subclass? (See my edit in a few minutes supplying current directory structures) – DSlomer64 May 03 '15 at 02:29
0

This is what finally worked for me. It is based on help from @CommonsWare here. Short answer: build variants. It is assumed that the two variants differ only slightly. In this example, one needs to be declared FREE while the other must be declared NOT FREE.

The two build variants in this example are named pro and free. They are not exactly variants of main, for which we define the usual resources, but very short classes encapsulating the only differences between the variants. Do NOT define ANY resources for one of the two build variants so that it will "inherit" those of main.

I chose free to have the resources of main. In order for the other build variant, pro, to have different name and icon (among other things), sufficient resources must be defined to provide differences from the other APK being built.

Using syntax similar to what is shown in build.gradle below, we declare both pro and free to be build variants inside a productFlavors block.

build.gradle:

apply plugin: 'com.android.application'

android
{
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig
    {
        applicationId "com.dslomer64.kakurocombosbuildvariants"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes
    {
        release
        {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    productFlavors
    {
        pro
        {
            applicationId "com.dslomer64.kakurocombosbuildvariants.pro"
        }
        free
        {
            applicationId "com.dslomer64.kakurocombosbuildvariants.free"
        }
    }

}

    dependencies
    {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.0.0'
    }

Note that the two build variants extend the package name from com.dslomer64.kakurocombosbuildvariants, appending either .pro or .free to get unique names. These necessarily-unique names are eventually uploaded to GPS.

AndroidManifest.xml (for main and free):

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

    package="com.dslomer64.kakurocombosbuildvariants" >

    <application

        android:icon="@mipmap/ic_launcher">

        android:allowBackup="true"
    >
    <activity
            android:screenOrientation="portrait"
            android:label="@string/app_name"

            android:icon="@mipmap/ic_launcher"

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

We will define a different app_name and a different ic_launcher for the two variants.

MyActivity.java contains almost all the code for the two slightly different apps. The only code contained in the other source apps is what distinguishes one from the other. In this case, it's one line each in a class is named Free in both APK sources. And MyActivity.java refers to that class and variable in what would seem an ambiguous way, except that gradle takes care of that in making the two builds.

Here's what the reference looks like in MyActivity.java:

`boolean FREE = Free.FREE;`

(I chose to use the same field name, FREE, in all 3 .java files AND to name the two classes Free. Probably not a good move. But it worked.)

Here's what the identical classes named Free look like:

KakuroCombosBuildVariants\app\src\**free**\java\com\dslomer64\kakurocombosbuildvariants\Free.java:

package com.dslomer64.kakurocombosbuildvariants;
public class Free
{
  public static final boolean FREE = true;
}

KakuroCombosBuildVariants\app\src\**pro**\java\com\dslomer64\kakurocombosbuildvariants\Free.java:

package com.dslomer64.kakurocombosbuildvariants;
public class Free
{
  public static final boolean FREE = false;
}

If you don't care about having different icons and app names, you're finished. Just click Build | Generate signed APKs ... and you'll see this:

enter image description here

enter image description here

enter image description here

And here are the TWO APKs: C:\Users\Dov\AndroidStudioProjects\KakuroCombosBuildVariants\app

enter image description here

Inside AS, you see this:

enter image description here

You are ready to upload both versions of the app to GPS. One at a time.

However, if you need different icons or names for the two apps, follow the same philosophy: let the resources for free be defined by main and define the different resources for pro under its directory node. Note NO res folder under free; it uses the res folder info in main:

Big picture:

enter image description here

Now note the complex res folder under pro, which defines its different name and icon, as well as its "java difference" from the free version.

enter image description here

Note that the mipmap icons for the different versions have the same NAME--ic_launcher--but that name is defined differently in the strings.xml file for the setup in main (for free) and in the strings.xml file for pro.

enter image description here

Same goes for the app_names, defined in main for free and in pro for itself.

enter image description here

For pro:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Kakuro Combos Pro </string>
</resources>

For free: enter image description here

Now is the other opportunity to upload the two APKs to GPS.

Community
  • 1
  • 1
DSlomer64
  • 4,234
  • 4
  • 53
  • 88