Project Configurations for Android π οΈ
Gradle configuration
- Gradle should be the default option for build system (for now, you may want to review some other later too such as Bazel, Buckβ¦)
- Although Gradle offers a large degree of flexibility in your project structure, unless you have a compelling reason to do otherwise, you should accept its default structure as this simplify your build scripts.
- Building a Signed Release APK, you can choose either way:
- AS Menu -> Build -> Generate Signed APK -> Manually adding the keystore directory / passwords -> Build.
-
Gradle config for Signing APK:
// Put this in your app module level gradle: signingConfigs { release { try { storeFile file("myapp.keystore") storePassword KEYSTORE_PASSWORD keyAlias "thekey" keyPassword KEY_PASSWORD } catch (ex) { throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.") } } } // And create another `gradle.properties` file to keep the password, don't PUSH this to the repo. KEYSTORE_PASSWORD=real_keystore_password KEY_PASSWORD=real_key_passw
-
Donβt ever expose the key secrets like this:
// Don't do this signingConfigs { release { storeFile file("myapp.keystore") storePassword "storepassword" keyAlias "thekey" keyPassword "nicepassword" } }
Gradle configuration for app flavors
The purpose is to separate the Develop environment (endpoints, library keysβ¦) with the real Production environment. To achieve this:
-
First, define the flavor in app level gradle config:
productFlavors { production { applicationId "com.app.android" } staging { // Staging AppId should be different from the real production. applicationId "com.app.staging" } } sourceSets { staging { res.srcDirs = ['src/staging/res'] } // production doesn't need to clarify the resource directory as it uses the default main one. }
-
Second, create a proper
staging
directory for the staging resources (endpoints, different keysβ¦):β β ββ main β β β ββ java β β β ββ res β β β ββ anim β β β ββ color β β β ββ drawable β β β ββ layout β β β ββ values β β β β β β β β ββ staging β β ββ res β β ββ anim β β ββ color β β ββ drawable β β ββ layout β β ββ values
ABI filter
- The ABI defines, with great precision, how an applicationβs machine code is supposed to interact with the system at runtime. You must specify an ABI for each CPU architecture you want your app to work with. Applying an incorrect ABI choice on a mismatched CPU architecture wonβt make it work. For example: running on
x86 emulator
requires you to use the properx86
apk. - You can read more from here, but in this example, weβre just simply splitting the app into 2 different flavors:
-
production_x86
for x86 chipset. -
production
for armeabi, armeabi-v7a chipset.
// Adding this to your app module level gradle: production_x86 { // Append automatically a different code version to identify the different version when app is published and we observe the tracking info. versionCode Integer.parseInt("6" + defaultConfig.versionCode) applicationId "com.redplanet.android" ndk { abiFilters "x86" } } production { applicationId "com.redplanet.android" ndk { abiFilters "armeabi", "armeabi-v7a" } } // Don't forget to bring the proper native library files (.so) to the main/jniLibs/
-
- Some Gradle tips that would be helpful.
Proguard
Proguard is normally used on Android projects to shrink and obfuscate the packaged code. We do this usually for release Apk (it helps saving time of downloading for users, hence increase the app download rates).
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Keystore for CI/CD
When distributing a debug build, either via an automated CI/CD process, or a manual build process (building APK locally
and send it to PM/Tester), we should create and configure a debug keystore
.
Having a debug keystore
removes a lot of issues for product managers, clients, and testers when installing a new (or old) build. Without it, the installation always requires uninstalling the existing version on the device to process. It happens to both Firebase App Distribution and manual installation.
-
To generate a debug key:
$ keytool -genkey -v -keystore debug.keystore -alias debug-key-alias -keyalg RSA -keysize 2048 -validity 10000
-
Then configure gradle for the debug flavor signing with the created key:
signingConfigs { debug { keyAlias "debug-key-alias" keyPassword System.getEnv("DEBUG_KEYSTORE_PASSWORD") storeFile file("debug.keystore") storePassword System.getEnv("DEBUG_KEYSTORE_PASSWORD") } ... } buildTypes { debug { signingConfig signingConfigs.debug } ... }
Phrase Configuration
Phrase is a platform we use to collaborate between developers, product managers, and clients in terms of localization and translation management.
Phrase is useful as a Single Source of Truth. Once we have any translations update, we can sync by pushing or pulling it with our repository through Phrase CLI
.
Setting up the Phrase Client
To set up you can check the Phrase CLI documentation for more details.
-
Create a
.phrase.yml
configuration file by running:$ phrase init
-
You will need to specify:
-
Your
access token
which you can create from the Translation Center in your profile settings: -
Your
Phrase project ID
: - A locale file format, for example:
values/strings.xml
values-th/strings.xml
- The location of your locale files inside your projectβs codebase.
-
Using Phrase
-
When you would like to update your locale files:
$ cd <your_project_directory> $ phrase pull
-
When you would like to push new strings or update the values of existing strings:
$ cd <your_project_directory> $ phrase push
You can set up the CI pipeline to push it automatically whenever you open a pull request:
name: Push strings to phrase on: pull_request: types: [ opened, edited, reopened ] branches-ignore: - 'release/**' - master jobs: push_strings_to_phrase: name: Push string to phrase runs-on: ubuntu-latest steps: - uses: actions/[email protected] - uses: winify-ag/[email protected] with: version: 2.0.12 - run: phrase push --wait - name: Uploading log run: | echo If you have some removed or changed keys, please check on <your_upload_log_link> to remove the unwanted keys.
-
When you would like to update only the string keys:
Phrase considers your updated key on locale file as a new key. So in case of updating the string key, you need to do it on Phrase website.
-
When you need to remove some strings, there are 2 options:
-
Remove by Phrase CLI:
-
Pull the Phrase localization first.
-
Remove the target string from the locale file.
-
Push the locale file with the string removed or open the pull request if you use the CI.
-
Go to
https://app.phrase.com/accounts/<your_account>/projects/<your_project>/uploads
and select the latest upload log: -
Click
Delete unmentioned keys
button. You will see the unmentioned string list, which contains the strings that are in the Phrase but arenβt in the strings.xml you uploaded: -
Click
Delete
button:
-
-
Remove on Phrase website:
-