How to build multiple APKs from a single source project - android

I wanna get multiple APKs from a single source project.
Just the application's title, icon, and package name are different with the others.
The project is on gradle(1.12), as below.
.
└── my_project
├── build.gradle
├── settings.gradle
└── module
├── build.gradle
└── src
How can I do that?

You can use productFlavors for that, and the under the promo and full folders (for example) create strings file (promo/res/values/strings.xml) with the update title value, same approach goes for the icon.
productFlavors {
promo {
packageName "com.woony.promo"
versionCode 1
versionName "v1.0.0_promo"
}
full {
packageName "com.woony"
versionCode 1
versionName "v1.0.0"
}
}
The updated project structure should be like the following
.
└── my_project
├── build.gradle
├── settings.gradle
└── module
├── build.gradle
└── src
├── main
├── promo
└── full
And to generate the release apks just call the following once (just make sure you added signingConfigs and linked it in your release buildTypes)
gradle assembleRelease

All the common files like java code, manifest and base resources are under "src/main"
Move your applications specific folders into "src/projects"
declare two productFlavor (one per applications) specifying applicationId and versionName
create two sourceSet (one per project) indicating the specific res, java, etc.. folders (i just needed res folder)
Full project's structure
Now, use Build Variants (bottom-left of Android Studio) to select the application to run

Related

Add dynamic feature module in a Eclipse Style Android project

I am trying to add dynamic feature module in my project. It was previously an Eclipse project so the structure is different from the Android Studio structure. The main application is in the root directory, not an independent module.
The project structure as follow:
/Project Root
Project Root Files
+Module1
+Dynamic Module
I want to add a dynamic feature module in the project, so I need to add the root project as the dependency of the dynamic module. Is there a way that I can do this? In the dynamic module build.gradle file, I tried ':Root' and ':', both did not work. Gradle said it could not resolve the root project.
Even I faced the above issue and was able to resolve it by referring the base module in the dependency module with the below approach.
dependencies {
implementation project(':')
}
If the base module is in the root of the project, one should refer the base module in the dependency module with the ":" symbol.
Using a project structure that gradle can deal with is the important bit here.
You can migrate the project Root to a different folder.
By convention that has been app.
You then can refer to it from dependent projects as :app.
The project structure would be then something like this:
.
├── build.gradle
├── app
│ ├── build.gradle
│ └── src
├── moduleA
│ ├── build.gradle
│ └── src
├── moduleB
│ ├── build.gradle
│ └── src

Adding layout resources to androidTest

I would like to add layout xml files into my androidTest folder to be used only for testing.
I added res/layout folder to androidTest and tried to add a layout file to it. But it gives error URI is not registered for xmlns:android="http://schemas.android.com/apk/res/android"
Somehow the project does not recognize it as valid layout file.
It is tricky to add xml resources to androidTest.
Android Instrumentation tests create another APK to run the tests against your original application. Although you can access your Context and objects from your main application, you cannot modify the generated APK file of your app.
That means you cannot put additional layout xml to your original application from tests that are in the androidTest folder.
Solution:
Alternatively,
you can create a buildType called espresso.
Then, create an espresso folder where you can put any java or Android resource you want to add.
You can even modify your AndroidManifest there.
Then, use testBuildType 'espresso'
Your build.gradle should look like this:
android {
testBuildType 'espresso'
buildTypes {
espresso.initWith(buildTypes.release)
}
}
dependencies {
espressoCompile 'somedependency' // you can even have special dependencies
}
When you run your espresso tests around that flavor, you will have an access to additional layout xml files you added.
Should look like this:
That's easy! In general, you should just put your resources under the src/androidTest/res folder. And that is! Then you can use it in your src/androidTest/java files. Yes, you can't use test layouts in your production APK, but you can use your test layouts in your test APK.
There're some problems that might confuse you. For instance autocompletion works well not so very often, but, anyway, it builds and works.
Recently I wrote custom control for masked EditText so I don't want to put any activity into the library, but I do want to have an activity to check the view and I do want inflate it from XML. You can see the whole code on the github page, here're some key moments:
$ tree androidTest/
androidTest/
├── AndroidManifest.xml
├── java
│   └── ru
│   └── egslava
│   └── lib_phone
│   ├── MainActivityTest.java
│   ├── TestActivity.java
│   └── actions
│   ├── HintViewAction.java
│   ├── KeepHintViewAction.java
│   └── SetTextViewAction.java
└── res
├── layout
│   └── activity_main.xml
└── values
└── styles.xml
So you can see, that under androidTest there's some kind of a separate project with its own manifest that registers Activity and so on :-) I would share more files, but it's just a project, no more and you always can look up the link.
The only thing that I'd like to warn you, that you should be ready that Android Studio will show you that your project contains errors even if that's not true :-) Good luck!
Cannot comment, but wanted to further add to #Slava's answer. If someone can add it as a comment, by all means.
Try suppressing the lint errors with the accepted answer from this question.
Android Studio Remove lint error

Android test raw resource

I have the following folder structure in Android Studio:
├── androidTest
│   ├── java
│   └── res
│   └── raw
│   └── test_file
└── main
├── java
└── res
└── raw
   └── app_file
I'm trying to access the test_file resource which exists in the raw folder of the androidTest elements. Here's the code inside a Robotium test case that inherits from ActivityInstrumentationTestCase2:
InputStream is = this.getInstrumentation()
.getContext()
.getResources()
.openRawResource(R.raw.test_file);
Android Studio throws a reference error since the resource cannot be found. The exact error is "Cannot resolve symbol test_file".
How can I reference this resource form a test case, which exists on the androidTest resources bundle?
By default your androidTest project will include your app's R class, but androidTest's resources will be generated into a separate file. Make sure you import the R class from your test project:
import com.your.package.test.R;
[..]
getInstrumentation().getContext().getResources().openRawResource(R.raw.test_file);
You can also directly reference the test project's R class:
getInstrumentation().getContext().getResources().openRawResource(com.your.package.test.R.raw.test_file);
I had the androidTest resources in the right spot (src/androidTest/res) and I still couldn't access them via <normal.package>.test.R. I spent a lot of time googling trying to figure out what was going on..
I FINALLY stumbled onto the answer. If you're building a buildType where you specified an applicationIdSuffix, your files are at <applicationId><applicationIdSuffix>.test.R !!!!
i.e.
applicationId "com.example.my.app"
buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
if you have androidTest resources in the right directory, then you can only access them via com.example.my.app.debug.test.R !!!!
See Android Unit Tests Requiring Context. For instrumentation test use InstrumentationRegistry.getInstrumentation().getTargetContext() (in Kotlin: getInstrumentation().targetContext). Then you can access resources. You won't need to import R file.
As the others have stated, androidTest resource ids are generated in <applicationId>.test.R by default. Since applicationId can be modified by different build types and flavors, this leads to different R files location for each of them. This can be changed by assigning explicit value to testApplicationId in the defaultConfig of the app's android configuration in build.gradle. It can be useful if there are more than one build types/flavors that alter the appId, but the tests can be run for any of them.
build.gradle (app):
android {
defaultConfig {
applicationId "com.example.hello"
testApplicationId = "com.example.hello.test"
}
}
Test files:
import com.example.hello.test.R

How do I get this project structure in Android Studio (AS) 1.1.0 to work to produce two APKs?

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:
Here's how it looks in AS: (EDITED!)(TWICE)
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.
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.
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:
And here are the TWO APKs:
C:\Users\Dov\AndroidStudioProjects\KakuroCombosBuildVariants\app
Inside AS, you see this:
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:
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.
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.
Same goes for the app_names, defined in main for free and in pro for itself.
For pro:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Kakuro Combos Pro </string>
</resources>
For free:
Now is the other opportunity to upload the two APKs to GPS.

How to create the best Android app project structure with Android Studio and Gradle?

I'm trying to switch to Android Studio and Gradle, but I have quite some issues with both integration into Studio and build with Gradle.
I've got an app that relies on several libraries.
I'd like to use Android Studio, and Gradle build system.
I'm using git
Most of my libraries are directly git cloned from their github location
Currently, what I have is:
Main Project
├── GH Lib 1
│ ├── <some stuff from the lib>
│ └── library
│ ├── AndroidManifest.xml
│ ├── res
│ └── src
├── GH Lib 2
│ └── <same structure as the lib 1>
├── GH Lib 3
│ └── <same structure as the lib 1>
│── GH Lib 4
│ └── <same structure as the lib 1>
└── My App folder
└── AndroidManifest.xml
└── res
└── src
└── libs
Each of the 'GH Lib X' directory is the result of a git clone from GitHub (for example: ActionBarSherlock).
'My app folder' contains directly res, src, AndroidManifest.xml, libs (with jars), etc.
1st question
I would like to understand how I can integrate all of this in Studio, with Gradle. Currently each lib is a module, and contains a build.gradle file. My App contains also a build.gradle file, however I can't reference dependencies from other folders, because they are in the parent folder, and this AFAIK can't be done with Gradle.
Would this structure be better?
My App Folder
│── AndroidManifest.xml
│── res
│── src
│── libs
└── dependencies
│── GH Lib 1
│── GH Lib 2
│── GH Lib 3
│── GH Lib 4
└── My App folder
My second question related to this is integration with git. Currently all libs are git submodules, is it a good idea?
You should look at the multiproject example for the layout attached in the doc.
http://tools.android.com/tech-docs/new-build-system
http://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA
Essentially you want a top level settings.gradle that tie all the pieces together. Ideally they should be in one single git repo to make your life easier. However you probably can use symlink to tie them into a common build repo that contain your top level settings.gradle.
This structure will work just fine. I have a similar structure and everything OK in Ubuntu 13.04 and Android Studio 130.729444.
You should provide settings.gradle file in root project with references (basically it's a relative path) to each module which should be built.
include ':my-app', ':gh-lib-1:library', ':gh-lib-2:library'
Root build.gradle file should contain tasks/configuration which will be common for all projects. More about multi-project setup can be found here: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-project-setup
Right now your source directories location does not conform to the default Android Studio setup. You can either move your src, res directories or setup sourceSets configuration in your build.gradle. It should be done for each project. More about project structure: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure
After these steps you may try to import your project to Android Studio by selecting root build.gradle file in the 'Import project' dialog.
It's possible that at first you will be unable to build the project in IDE due to Task 'assemble' not found in root project error. This is a bug in Android Studio. Fortunately, there is a workaround for this - just include task assemble{} in build.gradle files for all 'root' projects.

Categories

Resources