Skip to content

feat(android): scaffold buildable app shell with Compose startup screen#88

Open
up-tandem wants to merge 1 commit intomainfrom
feat/80-android-app-shell
Open

feat(android): scaffold buildable app shell with Compose startup screen#88
up-tandem wants to merge 1 commit intomainfrom
feat/80-android-app-shell

Conversation

@up-tandem
Copy link

@up-tandem up-tandem commented Feb 23, 2026

Summary

  • Add AndroidManifest.xml, MainActivity with Material3 Compose UI, and string/theme resources
  • Configure Compose compiler (BOM 2024.09.02, compiler extension 1.5.14) with JVM 17 target
  • Downgrade Kotlin 1.9.25 → 1.9.24 for Compose compiler compatibility
  • Document local build commands (assembleDebug, installDebug, ktlintCheck) in README

Test plan

  • ./gradlew :app:assembleDebug builds successfully
  • ./gradlew ktlintCheck passes with no violations
  • App launches on emulator/device showing "Forge Android" centered text

Resolves #80 (Android portion)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Added prerequisites, build instructions, and linting guidelines for Android development.
  • New Features

    • Initialized Android application with Material Design 3 support and Jetpack Compose framework.
  • Chores

    • Configured Java 17 compatibility and updated build system dependencies for Android.

Add AndroidManifest, MainActivity with Material3 Compose UI, string/theme
resources, Compose BOM and compiler config. Downgrade Kotlin to 1.9.24 for
Compose compiler compatibility. Document local build commands in README.

Resolves #80 (Android portion)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Walkthrough

Establishes a buildable Android app shell by configuring Gradle build settings (Jetpack Compose, Java 17, Kotlin compiler), adding manifest and activity wiring, defining theme and string resources, and documenting build prerequisites and commands.

Changes

Cohort / File(s) Summary
Build Configuration
mobile/android/build.gradle.kts, mobile/android/app/build.gradle.kts, mobile/android/gradle.properties
Enables Jetpack Compose, sets Java 17 and Kotlin 1.9.24 compatibility, adds Compose BOM (2024.09.02) dependency, configures AndroidX and Kotlin code style.
App Resources
mobile/android/app/src/main/res/values/strings.xml, mobile/android/app/src/main/res/values/themes.xml, mobile/android/app/src/main/AndroidManifest.xml
Defines app name "Forge", Material Light theme (Theme.Forge), and manifest entry for MainActivity with MAIN/LAUNCHER intent-filter.
App Code
mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt
Implements MainActivity using Jetpack Compose ComponentActivity with centered "Forge Android" Text display using MaterialTheme.
Documentation
mobile/android/README.md
Adds Prerequisites, Build, and Lint sections documenting Java 17+, Android SDK 34, Gradle tasks, and ktlintCheck instructions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: scaffolding a buildable Android app with Compose startup screen, which aligns with the PR's core objective.
Linked Issues check ✅ Passed The PR fulfills issue #80's Android requirements: buildable app shell with manifest, activity, resources, Compose startup screen, and documented build commands.
Out of Scope Changes check ✅ Passed All changes are in-scope for bootstrapping the Android app shell; no unrelated modifications to other platforms or business logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/80-android-app-shell

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mobile/android/app/build.gradle.kts (1)

13-13: ⚠️ Potential issue | 🟠 Major

targetSdk = 34 blocks Google Play submission.

Google Play has required all new app submissions and updates to target API level 35 since August 31, 2025. The current setting violates this mandatory requirement. Change targetSdk = 35 to comply and also unlock enableEdgeToEdge() enforcement (see the MainActivity comment).

🔧 Fix
-    targetSdk = 34
+    targetSdk = 35
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/build.gradle.kts` at line 13, The build.gradle.kts
currently sets targetSdk = 34 which prevents Google Play submission; update the
value to targetSdk = 35 in the Android module so the app targets API level 35,
then re-enable edge-to-edge enforcement referenced by enableEdgeToEdge() (and
remove the temporary bypass noted in MainActivity) so UI behavior matches the
new target; verify no other gradle properties or manifest settings conflict with
API 35 after changing targetSdk.
🧹 Nitpick comments (6)
mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt (1)

16-16: Add enableEdgeToEdge() before setContent {}.

enableEdgeToEdge() is the standard call for new Compose activities and becomes mandatory when targetSdk is bumped to 35. Adding it now avoids a required follow-up.

♻️ Proposed fix
+import androidx.activity.enableEdgeToEdge
 ...
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
+    enableEdgeToEdge()
     setContent {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt` at line
16, Add a call to enableEdgeToEdge() in MainActivity.onCreate before the
setContent { ... } invocation to opt into edge-to-edge window insets handling;
locate the onCreate method in MainActivity and insert enableEdgeToEdge() just
after super.onCreate(savedInstanceState) and before setContent so Compose uses
edge-to-edge behaviour (this prevents future breakage when targetSdk is bumped).
mobile/android/app/src/main/res/values/themes.xml (1)

3-3: Consider using Theme.MaterialComponents.DayNight.NoActionBar as the base theme.

android:Theme.Material.Light.NoActionBar is the legacy platform theme. Since material3 is already on the classpath (via BOM), Theme.MaterialComponents.DayNight.NoActionBar is a more idiomatic base for a Material3 Compose app and automatically handles some window/inset system-bar defaults more gracefully.

♻️ Proposed change
-  <style name="Theme.Forge" parent="android:Theme.Material.Light.NoActionBar" />
+  <style name="Theme.Forge" parent="Theme.MaterialComponents.DayNight.NoActionBar" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/src/main/res/values/themes.xml` at line 3, Update the app
theme style "Theme.Forge" to use the Material Components DayNight base by
changing its parent from "android:Theme.Material.Light.NoActionBar" to
"Theme.MaterialComponents.DayNight.NoActionBar" so the app leverages
Material3/BOM behavior and better system-bar/window defaults; locate the <style
name="Theme.Forge" parent="..."> entry and replace the parent attribute
accordingly.
mobile/android/app/build.gradle.kts (2)

22-24: composeOptions block is only needed for Kotlin < 2.0 — remove when upgrading Kotlin.

As of Kotlin 2.0, the Compose compiler is managed alongside the Kotlin compiler. In versions lower than Kotlin 2.0, consult the Compose to Kotlin Compatibility Map to identify a compatible compiler version. If the project is upgraded to Kotlin 2.0+ (suggested in the root build.gradle.kts comment), this block and kotlinOptions.jvmTarget can both be removed and replaced with kotlin { jvmToolchain(17) }.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/build.gradle.kts` around lines 22 - 24, The composeOptions
block setting kotlinCompilerExtensionVersion is no longer needed once Kotlin is
upgraded to 2.0+; remove the composeOptions block (the
kotlinCompilerExtensionVersion assignment) and also remove
kotlinOptions.jvmTarget, and replace them with the Kotlin toolchain
configuration by adding kotlin { jvmToolchain(17) } in the relevant Gradle
Kotlin DSL configuration (where kotlinOptions and composeOptions currently
appear) so the Compose compiler is managed by the Kotlin compiler.

37-37: Update Compose BOM to the latest stable release 2026.01.01.

The current BOM 2024.09.02 is ~17 months old. The latest stable release was published on January 29, 2026.

♻️ Proposed update
-  implementation(platform("androidx.compose:compose-bom:2024.09.02"))
+  implementation(platform("androidx.compose:compose-bom:2026.01.01"))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/build.gradle.kts` at line 37, Update the Compose BOM
version string used in the build script: locate the
implementation(platform("androidx.compose:compose-bom:2024.09.02")) entry and
change the BOM coordinate to "androidx.compose:compose-bom:2026.01.01" so the
project uses the latest stable Compose BOM; ensure you update any dependency
constraints or lockfiles if present and re-sync the Gradle project to validate
compatibility.
mobile/android/app/src/main/AndroidManifest.xml (1)

9-12: android:theme on the <activity> is redundant when it matches the <application> theme.

Activities inherit the <application> theme by default. Declaring the same @style/Theme.Forge on both is harmless but adds noise.

♻️ Proposed cleanup
     <activity
       android:name=".MainActivity"
-      android:exported="true"
-      android:theme="@style/Theme.Forge">
+      android:exported="true">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/src/main/AndroidManifest.xml` around lines 9 - 12, Remove
the redundant android:theme attribute from the <activity> entry for MainActivity
since it duplicates the application theme; locate the <activity
android:name=".MainActivity"> element in the AndroidManifest and delete the
android:theme="@style/Theme.Forge" attribute so the activity inherits the
application theme by default.
mobile/android/build.gradle.kts (1)

3-3: Update to Kotlin 2.3.10 to adopt the merged Compose compiler plugin and remove version friction.

Compose compiler 1.5.14 targets Kotlin 1.9.24, so the current pairing is correct. However, the Compose compiler has been merged into the Kotlin repository since Kotlin 2.0.0, so it ships simultaneously with Kotlin and is always compatible with the same Kotlin version. Staying on Kotlin 1.9.x in 2026 creates unnecessary migration debt—Kotlin 2.3.10 is the current stable release as of February 2026.

Upgrading to Kotlin 2.3.10 also lets you drop composeOptions.kotlinCompilerExtensionVersion in app/build.gradle.kts (see comment there) and replace this plugin with the new org.jetbrains.kotlin.plugin.compose Gradle plugin:

♻️ Kotlin 2.3.10 migration sketch
 plugins {
   id("com.android.application") version "8.5.2" apply false
-  id("org.jetbrains.kotlin.android") version "1.9.24" apply false
+  id("org.jetbrains.kotlin.android") version "2.3.10" apply false
+  id("org.jetbrains.kotlin.plugin.compose") version "2.3.10" apply false
   id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/build.gradle.kts` at line 3, Update the top-level Kotlin
plugin from id("org.jetbrains.kotlin.android") version "1.9.24" to use Kotlin
2.3.10 and the new Compose plugin identifier: replace
id("org.jetbrains.kotlin.android") version "1.9.24" apply false with
id("org.jetbrains.kotlin.plugin.compose") version "2.3.10" apply false; then
remove the now-unnecessary composeOptions.kotlinCompilerExtensionVersion entry
from app/build.gradle.kts and ensure module-level builds apply the new
org.jetbrains.kotlin.plugin.compose plugin where Compose is used so the Compose
compiler is provided by the Kotlin 2.3.10 runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mobile/android/app/src/main/AndroidManifest.xml`:
- Around line 4-8: The <application> element is missing launcher icon attributes
so the app shows the default AOSP icon; add android:icon and android:roundIcon
attributes (e.g., android:icon="@mipmap/ic_launcher" and
android:roundIcon="@mipmap/ic_launcher_round") to the <application> tag and
create corresponding adaptive icon resources (mipmap/ic_launcher.xml and
mipmap/ic_launcher_round.xml plus foreground/background drawables in
mipmap-anydpi-v26 and legacy PNGs in appropriate mipmap folders) so the launcher
uses your adaptive icons; update resource names in AndroidManifest.xml to match
the created files.

In `@mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt`:
- Around line 27-30: Replace the hardcoded Text("Forge Android") in
MainActivity.kt with a string resource: add a new entry (e.g., <string
name="splash_label">Forge Android</string>) to your strings.xml, then change the
Text composable to use stringResource(R.string.splash_label) (or update
R.string.app_name if you prefer to reuse it); update imports to include
androidx.compose.ui.res.stringResource if needed so the Text uses the localized
resource instead of a hardcoded literal.

---

Outside diff comments:
In `@mobile/android/app/build.gradle.kts`:
- Line 13: The build.gradle.kts currently sets targetSdk = 34 which prevents
Google Play submission; update the value to targetSdk = 35 in the Android module
so the app targets API level 35, then re-enable edge-to-edge enforcement
referenced by enableEdgeToEdge() (and remove the temporary bypass noted in
MainActivity) so UI behavior matches the new target; verify no other gradle
properties or manifest settings conflict with API 35 after changing targetSdk.

---

Nitpick comments:
In `@mobile/android/app/build.gradle.kts`:
- Around line 22-24: The composeOptions block setting
kotlinCompilerExtensionVersion is no longer needed once Kotlin is upgraded to
2.0+; remove the composeOptions block (the kotlinCompilerExtensionVersion
assignment) and also remove kotlinOptions.jvmTarget, and replace them with the
Kotlin toolchain configuration by adding kotlin { jvmToolchain(17) } in the
relevant Gradle Kotlin DSL configuration (where kotlinOptions and composeOptions
currently appear) so the Compose compiler is managed by the Kotlin compiler.
- Line 37: Update the Compose BOM version string used in the build script:
locate the implementation(platform("androidx.compose:compose-bom:2024.09.02"))
entry and change the BOM coordinate to "androidx.compose:compose-bom:2026.01.01"
so the project uses the latest stable Compose BOM; ensure you update any
dependency constraints or lockfiles if present and re-sync the Gradle project to
validate compatibility.

In `@mobile/android/app/src/main/AndroidManifest.xml`:
- Around line 9-12: Remove the redundant android:theme attribute from the
<activity> entry for MainActivity since it duplicates the application theme;
locate the <activity android:name=".MainActivity"> element in the
AndroidManifest and delete the android:theme="@style/Theme.Forge" attribute so
the activity inherits the application theme by default.

In `@mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt`:
- Line 16: Add a call to enableEdgeToEdge() in MainActivity.onCreate before the
setContent { ... } invocation to opt into edge-to-edge window insets handling;
locate the onCreate method in MainActivity and insert enableEdgeToEdge() just
after super.onCreate(savedInstanceState) and before setContent so Compose uses
edge-to-edge behaviour (this prevents future breakage when targetSdk is bumped).

In `@mobile/android/app/src/main/res/values/themes.xml`:
- Line 3: Update the app theme style "Theme.Forge" to use the Material
Components DayNight base by changing its parent from
"android:Theme.Material.Light.NoActionBar" to
"Theme.MaterialComponents.DayNight.NoActionBar" so the app leverages
Material3/BOM behavior and better system-bar/window defaults; locate the <style
name="Theme.Forge" parent="..."> entry and replace the parent attribute
accordingly.

In `@mobile/android/build.gradle.kts`:
- Line 3: Update the top-level Kotlin plugin from
id("org.jetbrains.kotlin.android") version "1.9.24" to use Kotlin 2.3.10 and the
new Compose plugin identifier: replace id("org.jetbrains.kotlin.android")
version "1.9.24" apply false with id("org.jetbrains.kotlin.plugin.compose")
version "2.3.10" apply false; then remove the now-unnecessary
composeOptions.kotlinCompilerExtensionVersion entry from app/build.gradle.kts
and ensure module-level builds apply the new org.jetbrains.kotlin.plugin.compose
plugin where Compose is used so the Compose compiler is provided by the Kotlin
2.3.10 runtime.

Comment on lines +4 to +8
<application
android:allowBackup="false"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Forge">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing android:icon and android:roundIcon — the launcher will show the default AOSP icon.

Add adaptive icon resources and declare them on the <application> element to complete the app shell:

  <application
    android:allowBackup="false"
+   android:icon="@mipmap/ic_launcher"
+   android:roundIcon="@mipmap/ic_launcher_round"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/Theme.Forge">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<application
android:allowBackup="false"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Forge">
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Forge">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/src/main/AndroidManifest.xml` around lines 4 - 8, The
<application> element is missing launcher icon attributes so the app shows the
default AOSP icon; add android:icon and android:roundIcon attributes (e.g.,
android:icon="@mipmap/ic_launcher" and
android:roundIcon="@mipmap/ic_launcher_round") to the <application> tag and
create corresponding adaptive icon resources (mipmap/ic_launcher.xml and
mipmap/ic_launcher_round.xml plus foreground/background drawables in
mipmap-anydpi-v26 and legacy PNGs in appropriate mipmap folders) so the launcher
uses your adaptive icons; update resource names in AndroidManifest.xml to match
the created files.

Comment on lines +27 to +30
Text(
text = "Forge Android",
style = MaterialTheme.typography.headlineMedium,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded splash text "Forge Android" should be a string resource.

R.string.app_name currently holds "Forge" (not "Forge Android"), so either add a dedicated resource (e.g., R.string.splash_label) or align app_name with the desired display text.

♻️ Proposed fix (after adding splash_label to strings.xml)

In strings.xml:

+  <string name="splash_label">Forge Android</string>

In MainActivity.kt:

+import androidx.compose.ui.res.stringResource
 ...
             Text(
-              text = "Forge Android",
+              text = stringResource(R.string.splash_label),
               style = MaterialTheme.typography.headlineMedium,
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/android/app/src/main/kotlin/com/forge/mobile/MainActivity.kt` around
lines 27 - 30, Replace the hardcoded Text("Forge Android") in MainActivity.kt
with a string resource: add a new entry (e.g., <string name="splash_label">Forge
Android</string>) to your strings.xml, then change the Text composable to use
stringResource(R.string.splash_label) (or update R.string.app_name if you prefer
to reuse it); update imports to include androidx.compose.ui.res.stringResource
if needed so the Text uses the localized resource instead of a hardcoded
literal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mobile): bootstrap native iOS and Android app shells

1 participant