Common Android Development Setup and Practices
Table of Contents
Initial Environment Setup
1.1. System Requirements
1.2. Setting Up the Development Environment
1.3. Configuring the Development Environment
1.4. Cloning the Repository and Project Setup
1.5. Troubleshooting Common Setup Issues
Running Your First Build
2.1. Building the Project
2.2. Running the Project
2.3. Configuring the Release Key
2.4. Switching Build Variants
Building the Application for Release or Testing
3.1. Updating Version Information
3.2. Selecting the Build Variant
3.3. Building the App Bundle
3.4. Generating a Signed App Bundle
3.5. Understanding Build Configurations and ProGuard Settings
Distributing Builds via Google Play Console
4.1. Navigating to the PokerSnowie App in Google Play Console
4.2. Managing Production Releases
4.3. Managing Internal Testing
UI Components Documentation
5.1. Overview
5.2. Understanding Jetpack Compose
Resources Directory
6.1. Overview
6.2. Drawable Resources
6.3. Font Resources
6.4. Raw Resources
Initial Environment Setup
System Requirements
Hardware Requirements
Windows:
OS: 64-bit Microsoft® Windows® 8/10/11
Processor: x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD CPU with support for a Windows Hypervisor
RAM: Minimum 8 GB (16 GB recommended)
Disk Space: 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
Resolution: 1280 x 800 minimum screen resolution
macOS:
OS: MacOS® 10.14 (Mojave) or higher
Processor: ARM-based chips, or 2nd generation Intel Core or newer with support for Hypervisor Framework
RAM: Minimum 8 GB (16 GB recommended)
Disk Space: 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
Resolution: 1280 x 800 minimum screen resolution
Linux:
OS: Any 64-bit Linux distribution that supports Gnome, KDE, or Unity DE; GNU C Library (glibc) 2.31 or later.
Processor: x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD processor with support for AMD Virtualization (AMD-V) and SSSE3
RAM: Minimum 8 GB (16 GB recommended)
Disk Space: 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
Resolution: 1280 x 800 minimum screen resolution
Software Requirements
Java Development Kit (JDK): JDK 11 or newer
Android Studio: The latest stable version
Setting Up the Development Environment
Installing Android Studio
Download Android Studio:
Visit the official Android Studio website at Android Studio to download the installer for your operating system.
Installation:
Follow the platform-specific instructions to install Android Studio and any recommended settings.
Windows: Run the
.exefile downloaded.macOS: Open the
.dmgfile and drag Android Studio to the Applications folder.Linux: Extract the
.tar.gzfile and run thestudio.shscript from theandroid-studio/bindirectory.
Initial Setup
Upon the first launch, follow the Android Studio setup wizard to install the Android SDK, Android SDK Platform-Tools, and Android SDK Build-Tools.
Configure an emulator by selecting an appropriate device profile and Android version in the AVD Manager.
Configuring the Development Environment
SDK Manager
Navigate to
Tools > SDK Managerto ensure the correct SDK platforms are installed for the Android versions you need to support.Install or update any additional SDK tools that are required.
Emulator Setup
Go to
Tools > AVD Managerand create a new Android Virtual Device (AVD).Choose a device definition (e.g., Pixel 3) and select the system image (e.g., Android 11).
Gradle Configuration
In
File > Project Structure > Project, set the Gradle version and Android Plugin version recommended for your version of Android Studio.Adjust
build.gradleto include dependencies and configure build variants specific to the PokerSnowie application.
Cloning the Repository and Project Setup
Clone the Repository:
Use VCS integration in Android Studio:
VCS > Git > Clone.Repository URL: PokerSnowie Repository
Project Configuration:
Allow Android Studio to import the project and auto-configure based on the existing
build.gradlefiles.Ensure all dependencies are correctly configured and up to date by referring to
build.gradle.
Troubleshooting Common Setup Issues
Check the SDK Manager for updates or missing components if you encounter issues with the Android SDK.
For Gradle build errors, verify the Gradle configuration in
build.gradleand ensure you are using compatible versions of the Android Gradle plugin.
Running Your First Build
Building the Project
Manual Build:
Navigate to
Build > Make Projectin the menu to compile the project and download the necessary dependencies.
Using Gradle:
Open the terminal within Android Studio and run
./gradlew assembleDebugto build the debug APK.
Running the Project
Using the Run Button:
Select the target device from the dropdown menu in the toolbar.
Click the "Play" button (▶) in the toolbar to run the app on the selected emulator or connected device.
Using the Menu:
Navigate to
Run > Run 'app'from the menu after selecting your target device to launch the application.
From the Terminal:
You can also run the application from the terminal within Android Studio by typing
./gradlew installDebugafter building the APK to install it on a connected device or active emulator.
Configuring Release Key
Release Key Setup:
A keystore file
PokerSnowieRelease.keystoreis provided in theandroid-pokersnowie/appdirectory. This file contains the keys used to sign your application in release mode.The signing configuration is specified in the
build.gradlefile within theandroidblock as follows:signingConfigs { release { storeFile file('******.keystore') storePassword '*******' keyAlias '*******' keyPassword '*******' } }
Switching Build Variants
To switch between debug and release build variants:
Open Android Studio and go to the "Build Variants" window, usually located on the left side of the workspace.
In the "Build Variants" panel, you will see a list of modules and their corresponding build variants. Select the
appmodule.Click on the dropdown menu under the "Active Build Variant" column for the
appmodule.Choose either
debugorreleaseto switch between development and production configurations. The IDE will automatically apply the change and re-sync the project.
This method allows you to test how your app behaves in both development and production-like environments, utilizing the appropriate build variant for your current development phase.
Building the Application for Release or Testing
Updating Version Information
Before creating a new release or debug build, you must update the version information to ensure each build is uniquely identifiable.
Update
build.gradle:Navigate to
app/build.gradle.In the
defaultConfigblock, update theversionCodeandversionName:defaultConfig { applicationId "com.pokersnowie.client.android" minSdk 24 targetSdk 34 versionCode 2000013 // Increment this for each new version versionName "2.1" // Update this to reflect the new version testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary true signingConfig signingConfigs.release }
Update User-Facing Version Information:
Navigate to
app/src/main/java/com.pokersnowie.client/android/pages/Options.At the bottom of the file, update the build number displayed to the user:
Text( text = "2.1 build 13", style = ExtraTypographyStyles["smallL"]!!, color = snwWarmGrey, modifier = Modifier.align(Alignment.Center) )
Selecting the Build Variant
To prepare your application for a release build or internal testing, you first need to select the appropriate build variant.
Open the "Build Variants" window:
Navigate to the "Build Variants" window in Android Studio, typically located at the bottom-left of the workspace.
Choose the Build Variant:
Find the
appmodule in the "Build Variants" panel.Use the dropdown menu under "Active Build Variant" to select either
debugfor testing orreleasefor the final release build.Android Studio will automatically apply the change and synchronize the project.
Building the App Bundle
After selecting the desired build variant, you need to build the App Bundle, which is the preferred format for distributing the application via the Google Play Store.
Build the App Bundle:
Navigate to
Build > Build Bundle(s) / APK(s) > Build Bundle(s).Choose the module for which you want to build the bundle if prompted.
Generating a Signed App Bundle
For the release build, you must generate a signed App Bundle. This ensures that the app is securely signed with your PokerSnowie release key.
Prepare the Keystore:
Ensure the keystore file,
PokerSnowieRelease.keystore, is located in theandroid-pokersnowie/appdirectory and properly referenced in yourbuild.gradlefile as previously set up.
Generate Signed Bundle:
Navigate to
Build > Generate Signed Bundle / APK.Select
Android App Bundleas the build type and clickNext.In the dialog that appears, specify the path to your keystore, the key alias, and enter the passwords for both the keystore and the key.
Choose the build variant (
release) and clickNext.Follow the prompts to complete the signing process.
Verify the Bundle:
After the signing process is complete, verify the generated App Bundle by locating it in the specified output folder, typically located in
android-pokersnowie/app/build/outputs/bundle/{release/debug}.
Understanding Build Configurations and ProGuard Settings
Build Types Configuration
The build.gradle file within the PokerSnowie project specifies different settings for debug and release build types to optimize and protect the application according to the needs of the development and production environments.
Debug Build Configuration:
debug { debuggable true buildConfigField "Boolean", "DEBUG_MODE", "true" }Release Build Configuration:
release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' }minifyEnabled true: Activates code shrinking, which removes unused code and resources, helping to reduce APK size and potentially deter reverse engineering.shrinkResources true: Further reduces APK size by aggressively removing unused resources.proguardFiles: Specifies ProGuard configuration files to control the obfuscation and optimization process, crucial for protecting the app from reverse engineering.
Proguard Configuration
ProGuard is a tool used to minify and obfuscate Java bytecode, which helps to protect your application against reverse engineering. The proguard-rules.pro file contains rules that define which parts of the code should be preserved from obfuscation. Here are key rules and their purposes:
General ProGuard Rules:
-keep class SGO.** { *; } -keep class Snowie.** { *; } -keepclassmembers class SGO.** { ... } -keepclassmembers class Snowie.** { ... }These rules ensure that classes and members related to the PokerSnowie engine, specifically the SGO and Snowie namespaces are not removed or renamed during the obfuscation process, which is crucial for maintaining functionality that relies on reflection or is accessed externally.
Specific Rule Examples:
-keep class kotlin.Metadata { *; } -keepattributes Signature, *Annotation*, EnclosingMethodkeep class: Prevents classes from being removed or renamed.
keepattributes: Ensures that certain attributes in the bytecode are preserved, which are necessary for Kotlin reflection and some Java functionalities.
Custom Rules for App Components:
-keep class com.pokersnowie.client.android.pages.ScoreboardKt { *; } -keep class com.pokersnowie.client.android.components.ScoreboardCardKt { *; }These rules protect specific app components and custom views from being obfuscated, ensuring that they function as expected without issues related to name mangling.
Make sure to review and update these configurations and rules as you add new features or dependencies to your application to maintain its integrity and performance.
Distributing Builds via Google Play Console
Navigating to the PokerSnowie App in Google Play Console
Access the Google Play Console:
Log in to your developer account at Google Play Console.
Select the PokerSnowie application from the list of apps under your developer account.
Managing Production Releases
For release builds intended for full public distribution, follow these steps to update the application in the Production track:
Go to Production:
Inside the PokerSnowie app dashboard, navigate to the Release section in the left sidebar.
Click on Production to manage the production releases.
Create a New Release:
Click on Create new release.
If prompted, review and accept any required agreements.
Upload the App Bundle:
Click on Upload to select the generated
.aabfile (Android App Bundle) from your local system.Once the upload is complete, the console will process the bundle and display the version details.
Set Release Details:
Provide release notes for this version which describe the changes or updates made.
Review and Send for Review:
Review all the details, then click Review release at the bottom of the page.
Once everything is confirmed, click Send for Review.
Google will review the submission. If approved, the build is automatically published due to managed publishing being off.
Managing Internal Testing
For debug builds, which are used for internal testing before a broader release:
Navigate to Internal Testing:
In the Release section, select Testing and then Internal testing.
Create a New Release for Internal Testing:
Click on Create new release.
Upload your debug App Bundle by following similar steps as in the Production release.
Specify Testers:
You can specify who can access this version by modifying the Pre-release Testers email list.
Add or confirm the emails of the testers who are authorized to install and test this build.
Retrieve the Testing Link:
Once the release is saved and reviewed, retrieve the link provided by Google Play Console.
Share this link with the testers so they can directly download and install the app on their devices.
UI Components Documentation
Overview
The UI components of the PokerSnowie application are built using Jetpack Compose, which is Android's modern toolkit for building native UI. This section covers the various directories and their respective files within the com.pokersnowie.client.android package, providing detailed documentation to help new developers understand the structure and functionality of the UI components.
Understanding Jetpack Compose
Jetpack Compose is a modern toolkit for building native Android UI. It simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs.
Key Concepts:
Composable Functions:
Functions annotated with
@Composablecan describe UI elements.These functions can be called from other composable functions, enabling the composition of complex UIs from smaller, reusable components.
Example:
@Composable fun Greeting(name: String) { Text(text = "Hello, $name!") } @Preview @Composable fun PreviewGreeting() { Greeting("Android") }@Previewallows you to preview the composable function in Android Studio without running the app.
State Management:
Jetpack Compose uses state to manage and update the UI. The UI automatically updates when the state changes.
The
rememberfunction is used to hold state across recompositions.The
mutableStateOffunction is used to create a mutable state.Copy code
@Composable fun Counter() { var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text(text = "Count: $count") } }
Layouts:
Compose provides a flexible way to build layouts using composable functions like
Column,Row,Box, etc.Example:
@Composable fun LayoutExample() { Column { Text("First Item") Row { Text("Second Item") Spacer(modifier = Modifier.width(16.dp)) Text("Third Item") } Box { Text("Fourth Item") } } }
Theming:
Jetpack Compose allows for easy theming of the UI using
MaterialTheme.You can define colours, typography, and shapes in a theme and apply it across the app.
Example:
@Composable fun ThemedApp() { MaterialTheme { Surface(color = MaterialTheme.colors.background) { Greeting("Themed World") } } }
Navigation:
Jetpack Compose provides a navigation library to handle in-app navigation.
NavHost and
NavControllerare used to set up and manage navigation between different composable screens.Example:
@Composable fun NavigationExample() { val navController = rememberNavController() NavHost(navController, startDestination = "home") { composable("home") { HomeScreen(navController) } composable("details") { DetailsScreen(navController) } } }
Advanced Jetpack Compose Concepts
1. Coroutines and State Management:
Coroutines are used for asynchronous programming in Kotlin. In Jetpack Compose, coroutines can be used to manage state and perform background tasks.
LaunchedEffect: Runs a coroutine when the key(s) change and cancels it when the composition leaves the composition tree.
Example:
@Composable fun ExampleLaunchedEffect() { var count by remember { mutableStateOf(0) } LaunchedEffect(Unit) { while (true) { delay(1000L) count++ } } Text(text = "Count: $count") }SideEffect: Used for non-suspending side effects in Compose. It runs after every successful recomposition.
Example:
@Composable fun ExampleSideEffect(count: Int) { SideEffect { println("Current count: $count") } Text(text = "Count: $count") }
2. MutableState and StateFlow:
MutableState: Holds a single value and updates the UI when the value changes.
Example:
@Composable fun ExampleMutableState() { var text by remember { mutableStateOf("Hello") } Button(onClick = { text = "World" }) { Text(text) } }StateFlow: A state-holder observable flow that emits the current and new state updates to its collectors.
Example:
class ExampleViewModel : ViewModel() { private val _uiState = MutableStateFlow("Hello") val uiState: StateFlow<String> = _uiState fun updateText(newText: String) { _uiState.value = newText } } @Composable fun ExampleStateFlow(viewModel: ExampleViewModel) { val text by viewModel.uiState.collectAsState() Button(onClick = { viewModel.updateText("World") }) { Text(text) } }
3. Remember and RememberSaveable:
remember: Used to store an object in the composition locally and retain it across recompositions.
Example:
@Composable fun ExampleRemember() { val count = remember { mutableStateOf(0) } Button(onClick = { count.value++ }) { Text("Count: ${count.value}") } }rememberSaveable: Similar to
rememberbut preserves the state across configuration changes (e.g., screen rotations).Example:
@Composable fun ExampleRememberSaveable() { val count = rememberSaveable { mutableStateOf(0) } Button(onClick = { count.value++ }) { Text("Count: ${count.value}") } }
Composable Functions and Performance:
CompositionLocal: Provides a way to propagate values down the compose tree without passing them as parameters.
Example:
val LocalExample = compositionLocalOf { "Default" } @Composable fun ExampleCompositionLocalProvider() { CompositionLocalProvider(LocalExample provides "Provided") { Text(LocalExample.current) } }Recomposition: Compose only recomposes parts of the UI that have changed, making it highly efficient.
Example:
@Composable fun ExampleRecomposition() { var count by remember { mutableStateOf(0) } Button(onClick = { count++ }) { Text("Count: $count") } }
5. Consuming Events with MutableStateFlow:
Events in Compose: Use
MutableStateFlowto handle one-time events such as showing the result of a socket function or navigating to another screen.Example:
class EventViewModel : ViewModel() { private val _eventFlow = MutableStateFlow<Event?>(null) val eventFlow: StateFlow<Event?> = _eventFlow fun triggerEvent(event: Event) { _eventFlow.value = event _eventFlow.value = null // Reset event after consumption } } @Composable fun EventObserver(viewModel: EventViewModel) { val event by viewModel.eventFlow.collectAsState() event?.let { // Handle the event (e.g., show a toast) } }
Resources Directory
Overview
The res directory in an Android project contains all the non-code resources used by the application, such as images, fonts, sounds, and animation files. These resources are organized into subdirectories based on their types.
Subdirectories and Their Roles
res/drawable: Contains image files (PNG, JPEG) and XML files for vector assets and other drawable resources.
res/font: Contains custom font files used in the application.
res/raw: Contains raw resource files such as sounds and JSON files for animations.
res/drawable
This directory holds various drawable resources, including images and XML files for vector graphics. These resources are used to display images and graphics in the application.
PNG/JPEG Files: Standard image files used for icons, backgrounds, and other graphical elements.
XML Files: Vector drawable resources defined using XML. These are scalable images often used for icons and other vector-based graphics.
res/font
This directory contains custom font files that the application uses for various text elements. These fonts enhance the visual appeal and readability of the text in the app.
res/raw
This directory holds raw resource files such as audio files and JSON files for animations. These files are used for various purposes within the application, such as playing sounds or displaying animations.
Audio Files: Used for sound effects, notifications, or background music.
JSON Files: Used for animations, particularly with libraries like Lottie for rendering vector animations.