changeset 47:829623189a57

[Gara] Android commit. Bazelfied it.
author MrJuneJune <me@mrjunejune.com>
date Sat, 13 Dec 2025 14:20:34 -0800
parents b9a40c633c93
children 46daba6e3cf4
files gara/android/firebase-cloud-messaging/.bazelrc gara/android/firebase-cloud-messaging/.bazelversion gara/android/firebase-cloud-messaging/BUILD gara/android/firebase-cloud-messaging/README.md gara/android/firebase-cloud-messaging/WORKSPACE gara/android/firebase-cloud-messaging/app/BUILD gara/android/firebase-cloud-messaging/app/google-services.json gara/android/firebase-cloud-messaging/app/src/main/AndroidManifest.xml gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MainActivity.java gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseInstanceIdService.java gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseMessagingService.java gara/android/firebase-cloud-messaging/app/src/main/res/drawable-v24/ic_launcher_foreground.xml gara/android/firebase-cloud-messaging/app/src/main/res/drawable/ic_launcher_background.xml gara/android/firebase-cloud-messaging/app/src/main/res/layout/activity_main.xml gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher.png gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher_round.png gara/android/firebase-cloud-messaging/app/src/main/res/values/colors.xml gara/android/firebase-cloud-messaging/app/src/main/res/values/strings.xml gara/android/firebase-cloud-messaging/app/src/main/res/values/styles.xml gara/android/jetpack-compose/.bazelversion gara/android/jetpack-compose/BUILD.bazel gara/android/jetpack-compose/MODULE.bazel gara/android/jetpack-compose/README.md gara/android/jetpack-compose/WORKSPACE gara/android/jetpack-compose/app/src/main/AndroidManifest.xml gara/android/jetpack-compose/app/src/main/BUILD.bazel gara/android/jetpack-compose/app/src/main/LibraryManifest.xml gara/android/jetpack-compose/app/src/main/java/com/example/android/bazel/MainActivity.kt gara/android/jetpack-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml gara/android/jetpack-compose/app/src/main/res/drawable/ic_launcher_background.xml gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher.png gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.png gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher.png gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.png gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.png gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png gara/android/jetpack-compose/app/src/main/res/values/colors.xml gara/android/jetpack-compose/app/src/main/res/values/strings.xml gara/android/jetpack-compose/app/src/main/res/values/styles.xml gara/android/ndk/.bazelrc gara/android/ndk/.bazelversion gara/android/ndk/BUILD.bazel gara/android/ndk/MODULE.bazel gara/android/ndk/README.md gara/android/ndk/WORKSPACE gara/android/ndk/app/src/main/AndroidManifest.xml gara/android/ndk/app/src/main/BUILD.bazel gara/android/ndk/app/src/main/LibraryManifest.xml gara/android/ndk/app/src/main/cpp/native-lib.cpp gara/android/ndk/app/src/main/java/com/example/android/bazel/MainActivity.java gara/android/ndk/app/src/main/res/drawable-v24/ic_launcher_foreground.xml gara/android/ndk/app/src/main/res/drawable/ic_launcher_background.xml gara/android/ndk/app/src/main/res/layout/activity_main.xml gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher.png gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher_round.png gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher.png gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher_round.png gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher.png gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png gara/android/ndk/app/src/main/res/values/colors.xml gara/android/ndk/app/src/main/res/values/strings.xml gara/android/ndk/app/src/main/res/values/styles.xml gara/android/ndk/images/graph.png gara/android/ndk/images/result.png gara/android/robolectric-testing/BUILD.bazel gara/android/robolectric-testing/MODULE.bazel gara/android/robolectric-testing/README.md gara/android/robolectric-testing/WORKSPACE gara/android/robolectric-testing/app/BUILD.bazel gara/android/robolectric-testing/app/src/main/AndroidManifest.xml gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/LoginActivity.kt gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/WelcomeActivity.kt gara/android/robolectric-testing/app/src/main/res/drawable-v24/ic_launcher_foreground.xml gara/android/robolectric-testing/app/src/main/res/drawable/ic_launcher_background.xml gara/android/robolectric-testing/app/src/main/res/layout/welcome_activity.xml gara/android/robolectric-testing/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml gara/android/robolectric-testing/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml gara/android/robolectric-testing/app/src/main/res/mipmap-hdpi/ic_launcher.png gara/android/robolectric-testing/app/src/main/res/mipmap-hdpi/ic_launcher_round.png gara/android/robolectric-testing/app/src/main/res/mipmap-mdpi/ic_launcher.png gara/android/robolectric-testing/app/src/main/res/mipmap-mdpi/ic_launcher_round.png gara/android/robolectric-testing/app/src/main/res/mipmap-xhdpi/ic_launcher.png gara/android/robolectric-testing/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png gara/android/robolectric-testing/app/src/main/res/mipmap-xxhdpi/ic_launcher.png gara/android/robolectric-testing/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png gara/android/robolectric-testing/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png gara/android/robolectric-testing/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png gara/android/robolectric-testing/app/src/main/res/values/strings.xml gara/android/robolectric-testing/app/src/test/java/com/example/android/bazel/WelcomeActivityTest.kt react_games/src/current.tsx
diffstat 104 files changed, 2325 insertions(+), 230 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/.bazelrc	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+common --enable_workspace
+common --experimental_google_legacy_api
+common --experimental_enable_android_migration_apis
+# Necesary until bazel 7.2.0rc2 or later is released (https://github.com/bazelbuild/bazel/issues/22415)
+common --nocheck_visibility
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/.bazelversion	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,1 @@
+7.2.0rc1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/BUILD	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,7 @@
+platform(
+    name = "arm64-v8a",
+    constraint_values = [
+        "@platforms//cpu:arm64",
+        "@platforms//os:android",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/README.md	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,96 @@
+
+# Bazel Firebase Cloud Messaging (FCM) example
+
+FCM requires certain information about your app (API key, app ID, project id,
+etc) to be present in the `res/values/values.xml` resource file. This example
+shows how to use the tools provided in the
+[bazelbuild/tools_android](https://github.com/bazelbuild/tools_android) repo to
+generate the `values.xml` file from the `google-services.json` file from your
+Firebase console.
+
+## Building the Example
+
+To build the example:
+
+1. Make sure the `ANDROID_HOME` environment variable is set to the absolute path
+   of your Android SDK.
+
+2. Go to the Firebase console for your project, and in Settings, download
+   `google-service.json`, and replace the sample file in the `app` directory.
+
+3. Run `bazel build //app` in the project.
+
+## Applying the Example to Your Code
+
+To apply this example to your code:
+
+1. Add the following to your `WORKSPACE` file:
+```python
+TOOLS_ANDROID_VERSION = "0.1"
+http_archive(
+  name = "tools_android",
+  strip_prefix = "tools_android-" + TOOLS_ANDROID_VERSION,
+  url = "https://github.com/bazelbuild/tools_android/archive/%s.tar.gz" % TOOLS_ANDROID_VERSION,
+)
+load("@tools_android//tools/googleservices:defs.bzl", "google_services_workspace_dependencies")
+google_services_workspace_dependencies()
+```
+
+2. Add the following to your `BUILD` file:
+```python
+load("@tools_android//tools/googleservices:defs.bzl", "google_services_xml")
+
+GOOGLE_SERVICES_XML = google_services_xml(
+  package_name = "com.example.myapplication",
+  google_services_json = "google-services.json"
+)
+```
+
+3. Add `GOOGLE_SERVICES_XML` to the `resource_files` attribute of your
+   `android_binary` rule. For example:
+```python
+android_binary(
+  ...
+  resource_files = glob(["src/main/res/**"]) + GOOGLE_SERVICES_XML,
+  ...
+)
+```
+
+4. Bazel's `AndroidManifest.xml` merging logic does not merge permissions from
+   dependent libraries (see issue [#5411](https://github.com/bazelbuild/bazel/issues/5411)).
+   You may need to add the following permissions to the `AndroidManifest.xml` of
+   your top-level `android_binary` rule:
+```xml
+<uses-permission android:name="android.permission.INTERNET" />
+<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+<uses-permission android:name="android.permission.WAKE_LOCK" />
+<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
+```
+
+## Manual Integration
+
+It's also possible to run the Google Services values.xml generator manually and
+add the results to your project:
+
+1. Go to the Firebase console for your project, and in Settings, download
+   `google-service.json`.
+
+2. From the workspace root of the `tools_android` project, run the Google
+   Services XML generator:
+```
+  bazel run //third_party/googleservices:GenerateGoogleServicesXml -- \
+     com.example.myapplication \
+     /absolute/path/to/google-services.json \
+     /tmp/values.xml
+```
+   The arguments are the package name for your app, the absolute file path to  
+   the `google-services.json` file, and finally the file path for `values.xml`.
+
+3. Merge the resulting `values.xml` file into your `values.xml` file (or put the
+   file into your `res/values` directory if you don't already have a
+   `values.xml` file). Alternatively, the `values.xml` file can be put into a
+   separate `res/values` directory and added to the `resource_files`. For the
+   example here, if `values.xml` is in
+   `app/src/main/google_services_xml/res/values/values.xml`, the `BUILD` file
+   would have
+   `resource_files = glob(["src/main/res/**"]) + ["src/main/google_services_xml/res/values/values.xml"],`.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/WORKSPACE	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,81 @@
+# FIXME(alexeagle): move to bzlmod
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+RULES_JVM_EXTERNAL_TAG = "5.3"
+
+RULES_JVM_EXTERNAL_SHA = "d31e369b854322ca5098ea12c69d7175ded971435e55c18dd9dd5f29cc5249ac"
+
+http_archive(
+    name = "rules_jvm_external",
+    sha256 = RULES_JVM_EXTERNAL_SHA,
+    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+    url =
+        "https://github.com/bazelbuild/rules_jvm_external/releases/download/%s/rules_jvm_external-%s.tar.gz" % (RULES_JVM_EXTERNAL_TAG, RULES_JVM_EXTERNAL_TAG),
+)
+
+load("@rules_jvm_external//:defs.bzl", "maven_install")
+
+maven_install(
+    aar_import_bzl_label = "@rules_android//rules:rules.bzl",
+    artifacts = [
+        "com.google.firebase:firebase-messaging:17.0.0",
+        "com.android.support:appcompat-v7:26.1.0",
+        "com.android.support.constraint:constraint-layout:1.0.2",
+        "com.google.code.gson:gson:2.8.2",
+    ],
+    # See https://github.com/bazelbuild/rules_jvm_external/#repository-aliases
+    # This can be removed if none of your external dependencies uses `maven_jar`.
+    generate_compat_repositories = True,
+    repositories = [
+        "https://jcenter.bintray.com",
+        "https://maven.google.com",
+        "https://repo1.maven.org/maven2",
+    ],
+    use_starlark_android_rules = True,
+    version_conflict_policy = "pinned",
+)
+
+load("@maven//:compat.bzl", "compat_repositories")
+
+compat_repositories()
+
+TOOLS_ANDROID_COMMIT = "0e864ba5a86958513658250de587416d8e17c481"
+
+http_archive(
+    name = "tools_android",
+    repo_mapping = {
+        "@com_google_code_gson_2_8_2": "@com_google_code_gson_gson",
+    },
+    sha256 = "57c50d6331ba578fc8adfcede20ef12b437cb4cc2edf60c852e4b834eefee5fc",
+    strip_prefix = "tools_android-" + TOOLS_ANDROID_COMMIT,
+    url = "https://github.com/bazelbuild/tools_android/archive/%s.tar.gz" % TOOLS_ANDROID_COMMIT,
+)
+
+RULES_ANDROID_COMMIT = "93e27030d3f0defa39cbbc35195638cb772b0c27"
+
+http_archive(
+    name = "rules_android",
+    sha256 = "71cae2413868a24f17d43fd595af6f3905d2e5b3235f76514f54800bfd90c903",
+    strip_prefix = "rules_android-" + RULES_ANDROID_COMMIT,
+    urls = ["https://github.com/bazelbuild/rules_android/archive/%s.zip" % RULES_ANDROID_COMMIT],
+)
+
+load("@rules_android//:prereqs.bzl", "rules_android_prereqs")
+
+rules_android_prereqs()
+
+load("@rules_android//:defs.bzl", "rules_android_workspace")
+
+rules_android_workspace()
+
+load("@rules_android//rules:rules.bzl", "android_sdk_repository")
+
+# Requires that the ANDROID_HOME environment variable is set to the Android SDK path.
+android_sdk_repository(
+    name = "androidsdk",
+)
+
+register_toolchains(
+    "@rules_android//toolchains/android:android_default_toolchain",
+    "@rules_android//toolchains/android_sdk:android_sdk_tools",
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/BUILD	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,29 @@
+load("@rules_android//android:rules.bzl", "android_binary")
+load("@tools_android//tools/googleservices:defs.bzl", "google_services_xml")
+
+GOOGLE_SERVICES_XML = google_services_xml(
+    package_name = "com.example.myapplication",
+    google_services_json = "google-services.json",
+)
+
+android_binary(
+    name = "app",
+    srcs = glob(["src/main/java/**/*.java"]),
+    # this sets the java package for the R class, since this android_binary
+    # rule isn't under a java root (i.e., some directory named "java" for the
+    # root of the java code for the app).
+    custom_package = "com.example.myapplication",
+    manifest = "src/main/AndroidManifest.xml",
+    manifest_values = {
+        "minSdkVersion": "15",
+        "applicationId": "com.example.myapplication",
+    },
+    resource_files = glob(["src/main/res/**"]) + GOOGLE_SERVICES_XML,
+    deps = [
+        "@maven//:com_google_firebase_firebase_messaging",
+        "@maven//:com_google_firebase_firebase_iid",
+        "@maven//:com_android_support_appcompat_v7",
+        # activity_main layout uses contraints
+        "@maven//:com_android_support_constraint_constraint_layout",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/google-services.json	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,42 @@
+{
+  "project_info": {
+    "project_number": "12345678901",
+    "firebase_url": "https://replace.with.your.project.example.com",
+    "project_id": "example-123",
+    "storage_bucket": "replace.with.your.project.example.org"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:12345678901:android:abcdef1111111111",
+        "android_client_info": {
+          "package_name": "com.example.myapplication"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "12345678901-abcdabcdabcdabcdabcdabcdabcdabcd.apps.googleusercontent.com",
+          "client_type": 1
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "abcdef12345678901_abcdef123456_abcdef12"
+        }
+      ],
+      "services": {
+        "analytics_service": {
+          "status": 1
+        },
+        "appinvite_service": {
+          "status": 1,
+          "other_platform_oauth_client": []
+        },
+        "ads_service": {
+          "status": 2
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/AndroidManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.myapplication">
+
+    <!-- Bazel doesn't merge perimissions from libraries / aars, so these are
+         necessary in the AndroidManifest.xml for the top-level android_binary -->
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name=".MyFirebaseInstanceIdService">
+            <intent-filter>
+                <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
+            </intent-filter>
+        </service>
+
+        <service
+            android:name=".MyFirebaseMessagingService">
+            <intent-filter>
+                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MainActivity.java	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,18 @@
+package com.example.myapplication;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+
+public class MainActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        Log.i("myapp", "token: " + FirebaseInstanceId.getInstance().getToken());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseInstanceIdService.java	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,21 @@
+package com.example.myapplication;
+
+import android.util.Log;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+
+public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
+    @Override
+    public void onTokenRefresh() {
+        // Get updated InstanceID token.
+        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+        Log.d("myapp", "Refreshed token: " + refreshedToken);
+
+        // If you want to send messages to this application instance or
+        // manage this apps subscriptions on the server side, send the
+        // Instance ID token to your app server.
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseMessagingService.java	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,39 @@
+package com.example.myapplication;
+
+import android.util.Log;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+public class MyFirebaseMessagingService extends FirebaseMessagingService {
+    @Override
+    public void onMessageReceived(RemoteMessage remoteMessage) {
+
+        // TODO(developer): Handle FCM messages here.
+        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+        Log.d("myapp", "From: " + remoteMessage.getFrom());
+
+        // Check if message contains a data payload.
+        if (remoteMessage.getData().size() > 0) {
+            Log.d("myapp", "Message data payload: " + remoteMessage.getData());
+
+            if (/* Check if data needs to be processed by long running job */ true) {
+                // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
+                //scheduleJob();
+            } else {
+                // Handle message within 10 seconds
+                //handleNow();
+            }
+
+        }
+
+        // Check if message contains a notification payload.
+        if (remoteMessage.getNotification() != null) {
+            Log.d("myapp", "Message Notification Body: " + remoteMessage.getNotification().getBody());
+        }
+
+        // Also if you intend on generating your own notifications as a result of a received FCM
+        // message, here is where that should be initiated. See sendNotification method below.
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/drawable-v24/ic_launcher_foreground.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/drawable/ic_launcher_background.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#26A69A"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/layout/activity_main.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.myapplication.MainActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</android.support.constraint.ConstraintLayout>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
Binary file gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed
Binary file gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/values/colors.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/values/strings.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">My Application</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/firebase-cloud-messaging/app/src/main/res/values/styles.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/.bazelversion	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,1 @@
+6.5.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,17 @@
+load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_compiler_plugin")
+
+kt_compiler_plugin(
+    name = "jetpack_compose_compiler_plugin",
+    id = "androidx.compose.compiler",
+    target_embedded_compiler = True,
+    visibility = ["//visibility:public"],
+    deps = ["@maven//:androidx_compose_compiler_compiler"],
+)
+
+platform(
+    name = "arm64-v8a",
+    constraint_values = [
+        "@platforms//cpu:arm64",
+        "@platforms//os:android",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/MODULE.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,38 @@
+"Bazel dependencies"
+
+bazel_dep(name = "platforms", version = "0.0.11")
+bazel_dep(name = "rules_jvm_external", version = "5.3")
+
+maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
+maven.install(
+    artifacts = [
+        "androidx.appcompat:appcompat:1.5.1",
+        # Jetpack Compose Dependencies
+        "androidx.activity:activity-compose:1.6.0",
+        "androidx.compose.material:material:1.2.1",
+        "androidx.compose.ui:ui:1.2.1",
+        "androidx.compose.ui:ui-tooling:1.2.1",
+        "androidx.compose.compiler:compiler:1.3.2",
+        "androidx.compose.runtime:runtime:1.2.1",
+        # Dependencies needed to manage version conflicts
+        "androidx.core:core:1.6.0",
+        "androidx.core:core-ktx:1.6.0",
+        "androidx.savedstate:savedstate-ktx:1.2.0",
+        "androidx.savedstate:savedstate:1.2.0",
+        "androidx.lifecycle:lifecycle-livedata-core-ktx:2.5.1",
+        "androidx.lifecycle:lifecycle-livedata-core:2.5.1",
+        "androidx.lifecycle:lifecycle-livedata:2.5.1",
+        "androidx.lifecycle:lifecycle-process:2.5.1",
+        "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1",
+        "androidx.lifecycle:lifecycle-runtime:2.5.1",
+        "androidx.lifecycle:lifecycle-service:2.5.1",
+        "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1",
+        "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1",
+        "androidx.lifecycle:lifecycle-viewmodel:2.5.1",
+    ],
+    repositories = [
+        "https://maven.google.com",
+        "https://repo1.maven.org/maven2",
+    ],
+)
+use_repo(maven, "maven")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/README.md	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+# Android Jetpack Compose with Bazel example
+
+## Documentation
+
+For the full documentation, please visit
+the [rules_kotlin documentation page](https://github.com/bazelbuild/rules_kotlin/blob/master/docs/kotlin.md).
+
+## Instructions
+
+1) Launch emulator
+2) Run `bazel mobile-install //app/src/main:app --start_app`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/WORKSPACE	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,42 @@
+# FIXME(alexeagle): move to bzlmod
+workspace(name = "bazel_android_sample_project")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+_KOTLIN_COMPILER_VERSION = "1.7.20"
+
+_KOTLIN_COMPILER_SHA = "5e3c8d0f965410ff12e90d6f8dc5df2fc09fd595a684d514616851ce7e94ae7d"
+
+## Android
+
+http_archive(
+    name = "build_bazel_rules_android",
+    sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
+    strip_prefix = "rules_android-0.1.1",
+    urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"],
+)
+
+load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository")
+
+android_sdk_repository(name = "androidsdk")
+
+## Kotlin
+
+http_archive(
+    name = "io_bazel_rules_kotlin",
+    sha256 = "f033fa36f51073eae224f18428d9493966e67c27387728b6be2ebbdae43f140e",
+    url = "https://github.com/bazelbuild/rules_kotlin/releases/download/v1.7.0-RC-3/rules_kotlin_release.tgz",
+)
+
+load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories", "kotlinc_version")
+
+kotlin_repositories(
+    compiler_release = kotlinc_version(
+        release = _KOTLIN_COMPILER_VERSION,
+        sha256 = _KOTLIN_COMPILER_SHA,
+    ),
+)
+
+load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains")
+
+kt_register_toolchains()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/AndroidManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.bazel">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,27 @@
+load("@build_bazel_rules_android//android:rules.bzl", "android_binary")
+load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library")
+
+kt_android_library(
+    name = "lib",
+    srcs = ["java/com/example/android/bazel/MainActivity.kt"],
+    custom_package = "com.example.android.bazel",
+    manifest = "LibraryManifest.xml",
+    plugins = ["//:jetpack_compose_compiler_plugin"],
+    resource_files = glob(["res/**/*"]),
+    deps = [
+        "@maven//:androidx_activity_activity_compose",
+        "@maven//:androidx_appcompat_appcompat",
+        "@maven//:androidx_compose_foundation_foundation",
+        "@maven//:androidx_compose_foundation_foundation_layout",
+        "@maven//:androidx_compose_runtime_runtime",
+        "@maven//:androidx_compose_ui_ui",
+        "@maven//:androidx_compose_ui_ui_tooling",
+    ],
+)
+
+android_binary(
+    name = "app",
+    manifest = "AndroidManifest.xml",
+    manifest_values = {"applicationId": "com.example.android.bazel"},
+    deps = [":lib"],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/LibraryManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.bazel">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/java/com/example/android/bazel/MainActivity.kt	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,38 @@
+package com.example.android.bazel
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+class MainActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent { HelloWorld("Jetpack Compose") }
+    }
+
+    @Preview
+    @Composable
+    fun HelloWorld(name: String) = Column(
+        verticalArrangement = Arrangement.Center,
+        horizontalAlignment = Alignment.CenterHorizontally,
+        modifier = Modifier
+            .fillMaxSize()
+            .padding(20.dp)) {
+        Text(
+            text = "Hello $name",
+            textAlign = TextAlign.Center
+        )
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/drawable/ic_launcher_background.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#26A69A"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed
Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/values/colors.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/values/strings.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Built By Bazel</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/jetpack-compose/app/src/main/res/values/styles.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/.bazelrc	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+common --enable_workspace
+common --experimental_google_legacy_api
+common --experimental_enable_android_migration_apis
+# Necesary until bazel 7.2.0rc2 or later is released (https://github.com/bazelbuild/bazel/issues/22415)
+common --nocheck_visibility
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/.bazelversion	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,2 @@
+7.3.1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,15 @@
+platform(
+    name = "arm64-v8a",
+    constraint_values = [
+        "@platforms//cpu:arm64",
+        "@platforms//os:android",
+    ],
+)
+
+platform(
+    name = "x86",
+    constraint_values = [
+        "@platforms//cpu:x86_32",
+        "@platforms//os:android",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/MODULE.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,30 @@
+"Bazel dependencies"
+
+bazel_dep(name = "platforms", version = "0.0.11")
+bazel_dep(name = "rules_jvm_external", version = "5.3")
+bazel_dep(name = "rules_cc", version = "0.0.9")
+bazel_dep(name = "rules_android", version = "0.5.1")
+
+maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
+maven.install(
+    aar_import_bzl_label = "@rules_android//rules:rules.bzl",
+    artifacts = [
+        "androidx.appcompat:appcompat:1.5.1",
+        "androidx.constraintlayout:constraintlayout:2.2.1",
+        # Needed to enforce version conflict resolution
+        "androidx.savedstate:savedstate:1.2.0",
+        "androidx.lifecycle:lifecycle-livedata-core:2.5.1",
+        "androidx.lifecycle:lifecycle-livedata:2.5.1",
+        "androidx.lifecycle:lifecycle-process:2.5.1",
+        "androidx.lifecycle:lifecycle-runtime:2.5.1",
+        "androidx.lifecycle:lifecycle-service:2.5.1",
+        "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1",
+        "androidx.lifecycle:lifecycle-viewmodel:2.5.1",
+    ],
+    repositories = [
+        "https://maven.google.com",
+        "https://repo1.maven.org/maven2",
+    ],
+    use_starlark_android_rules = True,
+)
+use_repo(maven, "maven")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/README.md	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,24 @@
+# Android NDK with Bazel example
+
+## Documentation
+
+For the full documentation, please visit the [Bazel documentation page](https://bazel.build/docs/android-ndk).
+
+## Instructions
+
+1) Launch emulator
+2) Run `bazel mobile-install //app/src/main:app --android_platforms=//:x86 --start_app`
+
+<img src="images/result.png" width="400px" />
+
+## Build graph
+
+![](/images/graph.png)
+
+- JNI/C++ sources goes into the `cc_library` target, `//app/src/main:jni_lib`.
+- Java sources, resource files, and assets go into the `android_library`
+  target, `//app/src/main:lib`. This target depends on the `cc_library` target.
+- The APK is built from the `android_binary` target, `//app/src/main:app`. This
+  target depends on the `android_library` target.
+
+NOTE: This graph omits the Google Maven AAR dependencies.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/WORKSPACE	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,43 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+RULES_ANDROID_COMMIT = "93e27030d3f0defa39cbbc35195638cb772b0c27"
+
+http_archive(
+    name = "rules_android",
+    sha256 = "71cae2413868a24f17d43fd595af6f3905d2e5b3235f76514f54800bfd90c903",
+    strip_prefix = "rules_android-" + RULES_ANDROID_COMMIT,
+    urls = ["https://github.com/bazelbuild/rules_android/archive/%s.zip" % RULES_ANDROID_COMMIT],
+)
+
+load("@rules_android//:prereqs.bzl", "rules_android_prereqs")
+
+rules_android_prereqs()
+
+load("@rules_android//:defs.bzl", "rules_android_workspace")
+
+rules_android_workspace()
+
+load("@rules_android//rules:rules.bzl", "android_sdk_repository")
+
+# Requires that the ANDROID_HOME environment variable is set to the Android SDK path.
+android_sdk_repository(
+    name = "androidsdk",
+)
+
+register_toolchains(
+    "@rules_android//toolchains/android:android_default_toolchain",
+    "@rules_android//toolchains/android_sdk:android_sdk_tools",
+)
+
+http_archive(
+    name = "rules_android_ndk",
+    sha256 = "b1a5ddd784e6ed915c2035c0db536a278b5f50c64412128c06877115991391ef",
+    strip_prefix = "rules_android_ndk-877c68ef34c9f3353028bf490d269230c1990483",
+    url = "https://github.com/bazelbuild/rules_android_ndk/archive/877c68ef34c9f3353028bf490d269230c1990483.zip",
+)
+
+load("@rules_android_ndk//:rules.bzl", "android_ndk_repository")
+
+android_ndk_repository(name = "androidndk")
+
+register_toolchains("@androidndk//:all")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/AndroidManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.bazel"
+    >
+
+    <uses-sdk android:minSdkVersion="23" />
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,27 @@
+load("@rules_android//android:rules.bzl", "android_binary", "android_library")
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+android_library(
+    name = "lib",
+    srcs = ["java/com/example/android/bazel/MainActivity.java"],
+    custom_package = "com.example.android.bazel",
+    manifest = "LibraryManifest.xml",
+    resource_files = glob(["res/**/*"]),
+    deps = [
+        ":jni_lib",
+        "@maven//:androidx_appcompat_appcompat",
+        "@maven//:androidx_constraintlayout_constraintlayout",
+    ],
+)
+
+cc_library(
+    name = "jni_lib",
+    srcs = ["cpp/native-lib.cpp"],
+)
+
+android_binary(
+    name = "app",
+    manifest = "AndroidManifest.xml",
+    manifest_values = {"applicationId": "com.example.android.bazel"},
+    deps = [":lib"],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/LibraryManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.bazel"
+    >
+
+    <uses-sdk android:minSdkVersion="23" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        >
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/cpp/native-lib.cpp	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,13 @@
+#include <jni.h>
+#include <string>
+
+extern "C"
+JNIEXPORT jstring
+
+JNICALL
+Java_com_example_android_bazel_MainActivity_stringFromJNI(
+        JNIEnv *env,
+        jobject /* this */) {
+    std::string hello = "Hello from C++";
+    return env->NewStringUTF(hello.c_str());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/java/com/example/android/bazel/MainActivity.java	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,28 @@
+package com.example.android.bazel;
+
+import android.os.Bundle;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+
+public class MainActivity extends AppCompatActivity {
+
+    static {
+        System.loadLibrary("app");
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        // Example of a call to a native method
+        TextView tv = (TextView) findViewById(R.id.sample_text);
+        tv.setText(stringFromJNI());
+    }
+
+    /**
+     * A native method that is implemented by the 'native-lib' native library,
+     * which is packaged with this application.
+     */
+    public native String stringFromJNI();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/drawable-v24/ic_launcher_foreground.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/drawable/ic_launcher_background.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#26A69A"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/layout/activity_main.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.android.bazel.MainActivity"
+    >
+
+    <TextView
+        android:id="@+id/sample_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
Binary file gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher_round.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed
Binary file gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/values/colors.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/values/strings.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Built By Bazel</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/ndk/app/src/main/res/values/styles.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
Binary file gara/android/ndk/images/graph.png has changed
Binary file gara/android/ndk/images/result.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,7 @@
+platform(
+    name = "arm64-v8a",
+    constraint_values = [
+        "@platforms//cpu:arm64",
+        "@platforms//os:android",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/MODULE.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,22 @@
+"Bazel dependencies"
+
+bazel_dep(name = "platforms", version = "0.0.11")
+bazel_dep(name = "rules_jvm_external", version = "5.3")
+
+maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
+maven.install(
+    artifacts = [
+        "org.robolectric:robolectric:4.9",
+        "junit:junit:4.13.2",
+        "com.google.truth:truth:1.1.3",
+        "org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10",
+        "org.jetbrains.kotlin:kotlin-stdlib:1.7.10",
+    ],
+    repositories = [
+        "https://maven.google.com",
+        "https://repo1.maven.org/maven2",
+    ],
+)
+use_repo(maven, "maven")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/README.md	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+# Android Jetpack Compose with Bazel example
+
+## Documentation
+
+For the full documentation, please visit
+the [robolectric-bazel documentation page](https://github.com/robolectric/robolectric-bazel#usage).
+
+## Instructions
+
+1) Launch emulator
+2) Run `bazel test //app:test`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/WORKSPACE	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,46 @@
+# FIXME(alexeagle): move to bzlmod
+workspace(name = "bazel_android_sample_project")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+## Android
+
+http_archive(
+    name = "build_bazel_rules_android",
+    sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
+    strip_prefix = "rules_android-0.1.1",
+    urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"],
+)
+
+load("@build_bazel_rules_android//android:rules.bzl", "android_sdk_repository")
+
+android_sdk_repository(name = "androidsdk")
+
+## Kotlin
+
+http_archive(
+    name = "io_bazel_rules_kotlin",
+    sha256 = "f033fa36f51073eae224f18428d9493966e67c27387728b6be2ebbdae43f140e",
+    url = "https://github.com/bazelbuild/rules_kotlin/releases/download/v1.7.0-RC-3/rules_kotlin_release.tgz",
+)
+
+load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories")
+
+kotlin_repositories()
+
+load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains")
+
+kt_register_toolchains()
+
+# Android Testing
+
+http_archive(
+    name = "robolectric",
+    sha256 = "7655c49633ec85a18b5a94b1ec36e250671808e45494194959b1d1d7f3e73a23",
+    strip_prefix = "robolectric-bazel-4.9",
+    urls = ["https://github.com/robolectric/robolectric-bazel/archive/4.9.tar.gz"],
+)
+
+load("@robolectric//bazel:robolectric.bzl", "robolectric_repositories")
+
+robolectric_repositories()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/BUILD.bazel	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,29 @@
+load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library", "kt_android_local_test")
+
+kt_android_library(
+    name = "lib",
+    srcs = glob(["src/main/java/**/*.kt"]),
+    custom_package = "com.example.android.bazel",
+    manifest = "src/main/AndroidManifest.xml",
+    resource_files = glob(["src/main/res/**"]),
+    deps = [
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_common",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_jdk7",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_jdk8",
+    ],
+)
+
+kt_android_local_test(
+    name = "test",
+    srcs = ["src/test/java/com/example/android/bazel/WelcomeActivityTest.kt"],
+    custom_package = "com.example.android.bazel.test",
+    test_class = "com.example.android.bazel.WelcomeActivityTest",
+    deps = [
+        ":lib",
+        "@maven//:com_google_truth_truth",
+        "@maven//:junit_junit",
+        "@maven//:org_robolectric_robolectric",
+        "@robolectric//bazel:android-all",
+    ],
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/AndroidManifest.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.bazel"
+    >
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        >
+
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".LoginActivity" />
+    </application>
+
+</manifest>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/LoginActivity.kt	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,11 @@
+package com.example.android.bazel
+
+import android.app.Activity
+import android.os.Bundle
+
+class LoginActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/WelcomeActivity.kt	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,20 @@
+package com.example.android.bazel
+
+import android.app.Activity
+import android.os.Bundle
+import android.view.View
+import android.content.Intent
+
+class WelcomeActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(R.layout.welcome_activity)
+
+        val button: View = findViewById(R.id.login)
+        button.setOnClickListener({ v ->
+            startActivity(Intent(this@WelcomeActivity, LoginActivity::class.java))
+        })
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/drawable-v24/ic_launcher_foreground.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/drawable/ic_launcher_background.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#26A69A"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/layout/welcome_activity.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/login"
+        android:text="Login"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-hdpi/ic_launcher_round.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed
Binary file gara/android/robolectric-testing/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/main/res/values/strings.xml	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Built By Bazel</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gara/android/robolectric-testing/app/src/test/java/com/example/android/bazel/WelcomeActivityTest.kt	Sat Dec 13 14:20:34 2025 -0800
@@ -0,0 +1,29 @@
+package com.example.android.bazel
+
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.robolectric.RobolectricTestRunner
+import org.junit.runner.RunWith
+import org.robolectric.Robolectric
+import org.robolectric.Shadows.shadowOf
+import android.content.Intent
+import org.robolectric.RuntimeEnvironment
+import android.app.Activity
+import android.view.View
+import org.junit.Assert.assertEquals
+
+@RunWith(RobolectricTestRunner::class)
+class WelcomeActivityTest {
+
+    @Test
+    fun clickingLogin_shouldStartLoginActivity() {
+        Robolectric.buildActivity(WelcomeActivity::class.java).use { controller ->
+            controller.setup() // Moves Activity to RESUMED state
+            val activity: Activity = controller.get()
+            activity.findViewById<View>(R.id.login).performClick()
+            val expectedIntent = Intent(activity, LoginActivity::class.java)
+            val actual: Intent = shadowOf(RuntimeEnvironment.application).getNextStartedActivity()
+            assertEquals(expectedIntent.getComponent(), actual.getComponent())
+        }
+    }
+}
--- a/react_games/src/current.tsx	Thu Dec 04 06:50:40 2025 -0800
+++ b/react_games/src/current.tsx	Sat Dec 13 14:20:34 2025 -0800
@@ -1,265 +1,379 @@
-import { CSSProperties, useEffect, useReducer, useState } from "react";
+import { CSSProperties, useReducer, useState, useRef, useEffect } from "react";
 import ReactDOM from "react-dom/client";
 
 /**
- * 2048
- * 
- * 4 X 4
+ * CONFIGURATION
+ * Replace this with your actual API key or fetch it from an environment variable.
  */
-
-const MAX_WIDTH = 4;
-
-type Color = 'white' | 'orange' | 'yellow' | 'red';
-
-type Cell = {
-  value: number;
-  color: Color;
-}
-
-type Board = Cell[][];
+const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY_HERE";
 
-type GameState = "in_progress" | "lost" | "won";
-
-type Game = {
-  board: Board;
-  state: GameState;
-  steps: number;
-}
-
-type Command = "u" | "d" | "l" | "r";
-
-type GameAction = 
-  { type: "move", command: Command } | { type: "calculate" };
-
-
-interface GameStyle {
-  container: CSSProperties;
-  board: CSSProperties;
-  cell: (color: Color) => CSSProperties;
+interface ChatStyle {
+  mainContainer: CSSProperties;
+  sideBar: CSSProperties;
+  sideBarItem: CSSProperties; // Added for hover/layout
+  mainChat: CSSProperties;
+  mainMessage: CSSProperties;
+  messageBubble: CSSProperties; // Added for styling messages
+  inputBar: CSSProperties;
 }
 
-const gameStyle: GameStyle = {
-  container: {
+const STYLES: ChatStyle = {
+  mainContainer: {
+    display: "flex",
+    backgroundColor: "#1e1e1e",
+    color: "white",
+    fontFamily: "sans-serif",
+    height: "100vh",
+    margin: 0,
+  },
+  sideBar: {
+    width: "250px",
+    height: "100vh",
+    overflowY: "auto",
+    backgroundColor: "#000000",
+    borderRight: "1px solid #333",
+    padding: "1rem",
+  },
+  sideBarItem: {
+    padding: "10px",
+    cursor: "pointer",
+    backgroundColor: "#333",
+    marginBottom: "5px",
+    borderRadius: "5px",
+    whiteSpace: "nowrap",
+    overflow: "hidden",
+    textOverflow: "ellipsis",
+  },
+  mainChat: {
+    flex: 1,
+    display: "flex",
+    flexDirection: "column",
+    backgroundColor: "#343541",
+    height: "100vh",
+  },
+  mainMessage: {
+    flex: 1,
+    padding: "20px",
+    overflowY: "auto",
     display: "flex",
     flexDirection: "column",
-    justifyContent: "center",
-    alignItems: "center",
-    height: "100vh"
+    gap: "15px",
+  },
+  messageBubble: {
+    padding: "15px",
+    borderRadius: "8px",
+    lineHeight: "1.5",
+    maxWidth: "800px",
+    margin: "0 auto",
+    width: "100%",
   },
-  board: {
-    display: "grid",
-    gridTemplateColumns: "repeat(4, 50px)",
-    background: "#EEFFEE",
+  inputBar: {
+    width: "100%",
+    height: "100px",
+    padding: "15px",
+    backgroundColor: "#40414f",
+    color: "white",
+    border: "none",
+    borderTop: "1px solid #555",
+    fontSize: "16px",
+    resize: "none",
+    outline: "none",
   },
-  cell: (color: Color) => ({
-    display: "flex",
-    justifyContent: "center",
-    alignItems: "center",
-    aspectRatio: "1 / 1 ",
-    margin: "10px",
-    background: color,
-  })
-}
+};
+
+type Chat = {
+  id: string;
+  title: string;
+  createdAt: number;
+};
+
+type ChatHistory = Chat[];
+
+type Message = {
+  id: string;
+  role: 'user' | 'assistant'; // Changed from 'author' to match OpenAI spec usually
+  content: string; // Changed from 'message' to 'content'
+  createdAt: number;
+};
+
+type MainPage = {
+  activeChatId: string | null;
+  chatHistory: ChatHistory;
+  currentMessages: Message[]; 
+  allChats: Record<string, Message[]>; 
+  sendMessageStatus: 'idle' | 'inProgress' | 'failed' | 'success';
+};
+
+// Expanded Actions
+type MainPageAction = 
+  | { type: 'select_chat'; payload: { chatId: string } }
+  | { type: 'user_message_sent'; payload: { content: string; newChatId?: string } }
+  | { type: 'api_response_received'; payload: { content: string } }
+  | { type: 'api_error'; payload: { error: string } };
+
+function mainPageDispatch(state: MainPage, action: MainPageAction): MainPage {
+  switch (action.type) {
+    case 'select_chat': {
+      const { chatId } = action.payload;
+      return {
+        ...state,
+        activeChatId: chatId,
+        currentMessages: state.allChats[chatId] || [],
+        sendMessageStatus: 'idle',
+      };
+    }
 
-function initializeBoard(): Board {
-  const board = Array.from({ length: MAX_WIDTH }, () =>
-    Array.from({ length: MAX_WIDTH }, (): Cell => ({ value: 0, color: 'orange' }))
-  );
-  let rowIndex: number;
-  let colIndex: number;
-  rowIndex = Math.floor(Math.random() * 4);
-  colIndex = Math.floor(Math.random() * 4);
-  board[rowIndex][colIndex].value = 2;
-  board[rowIndex-1][colIndex].value = 2;
-  return board;
-}
+    case 'user_message_sent': {
+      const { content, newChatId } = action.payload;
+      
+      const newMessage: Message = {
+        id: Date.now().toString(),
+        role: 'user',
+        content: content,
+        createdAt: Date.now(),
+      };
+
+      // If we are starting a brand new chat (no active ID)
+      if (newChatId && !state.activeChatId) {
+        const newChatMetadata: Chat = {
+          id: newChatId,
+          title: content.substring(0, 30) + (content.length > 30 ? "..." : ""),
+          createdAt: Date.now()
+        };
+
+        return {
+          ...state,
+          activeChatId: newChatId,
+          sendMessageStatus: 'inProgress',
+          chatHistory: [newChatMetadata, ...state.chatHistory],
+          currentMessages: [newMessage],
+          allChats: {
+            ...state.allChats,
+            [newChatId]: [newMessage]
+          }
+        };
+      } 
+      
+      const chatId = state.activeChatId!;
+      const updatedMessages = [...state.currentMessages, newMessage];
 
-function initializeGame(): Game {
-  return {
-    board: initializeBoard(),
-    state: "in_progress",
-    steps: 0,
+      return {
+        ...state,
+        sendMessageStatus: 'inProgress',
+        currentMessages: updatedMessages,
+        allChats: {
+          ...state.allChats,
+          [chatId]: updatedMessages
+        }
+      };
+    }
+
+    case 'api_response_received': {
+      if (!state.activeChatId) return state;
+
+      const newMsg: Message = {
+        id: Date.now().toString(),
+        role: 'assistant',
+        content: action.payload.content,
+        createdAt: Date.now()
+      };
+
+      const updatedMessages = [...state.currentMessages, newMsg];
+
+      return {
+        ...state,
+        sendMessageStatus: 'success',
+        currentMessages: updatedMessages,
+        allChats: {
+          ...state.allChats,
+          [state.activeChatId]: updatedMessages
+        }
+      };
+    }
+
+    case 'api_error': {
+      return {
+        ...state,
+        sendMessageStatus: 'failed'
+      };
+    }
+
+    default:
+      return state;
   }
 }
 
-
-function handleMove(board: Board, command: Command): Board {
-  // Deep copy the board and initialize the merged status for the new board
-  const copiedBoard = board.map(row => 
-    row.map(cell => ({ ...cell, merged: false }))
-  );
-
-  let diff: { row: number, col: number };
-  let startRow: number, endRow: number, stepRow: number;
-  let startCol: number, endCol: number, stepCol: number;
-
-  const size = copiedBoard.length;
-
-  switch (command) {
-    case "u": 
-      diff = { row: -1, col: 0 };
-      startRow = 0; endRow = size; stepRow = 1;
-      startCol = 0; endCol = size; stepCol = 1;
-      break;
-    case "d": 
-      diff = { row: 1, col: 0 };
-      startRow = size - 1; endRow = -1; stepRow = -1;
-      startCol = 0; endCol = size; stepCol = 1;
-      break;
-    case "l":
-      diff = { row: 0, col: -1 };
-      startRow = 0; endRow = size; stepRow = 1;
-      startCol = 0; endCol = size; stepCol = 1;
-      break;
-    case "r":
-      diff = { row: 0, col: 1 };
-      startRow = 0; endRow = size; stepRow = 1;
-      startCol = size - 1; endCol = -1; stepCol = -1;
-      break;
-  }
-
-  for (let rowIndex = startRow; rowIndex !== endRow; rowIndex += stepRow) {
-    for (let colIndex = startCol; colIndex !== endCol; colIndex += stepCol) {
-      const currentCell = copiedBoard[rowIndex][colIndex];
-      
-      if (currentCell.value === 0) continue;
-
-      let r = rowIndex;
-      let c = colIndex;
-      let emptySlot: { r: number, c: number } = { r: rowIndex, c: colIndex };
-      let finalSlot: { r: number, c: number } = { r: rowIndex, c: colIndex };
-
-      while (true) {
-        r += diff.row;
-        c += diff.col;
-
-        if (r < 0 || r >= size || c < 0 || c >= size) {
-          finalSlot = emptySlot;
-          break;
-        }
-
-        const nextCell = copiedBoard[r][c];
-        
-        if (nextCell.value === 0) {
-          emptySlot = { r, c };
-          finalSlot = emptySlot;
-        } else if (nextCell.value === currentCell.value && !nextCell.merged) {
-          finalSlot = { r, c };
-          break;
-        } else {
-          finalSlot = emptySlot; 
-          break;
-        }
-      }
-
-      const targetCell = copiedBoard[finalSlot.r][finalSlot.c];
-
-      if (finalSlot.r === rowIndex && finalSlot.c === colIndex) {
-        continue;
-      }
-      
-      if (targetCell.value === currentCell.value && !targetCell.merged) {
-        targetCell.value *= 2;
-        targetCell.merged = true;
-        
-        copiedBoard[rowIndex][colIndex].value = 0;
-        
-      } else if (targetCell.value === 0) {
-        targetCell.value = currentCell.value;
-        copiedBoard[rowIndex][colIndex].value = 0;
-      }
-    }
-  }
-
-  return copiedBoard;
-}
-
-function addNewItemsToTheBoard(board: Board) { 
-  let randomRowIndex: number;
-  let randomColIndex: number;
+const initialMainPage: MainPage = {
+  activeChatId: null,
+  chatHistory: [],
+  currentMessages: [],
+  allChats: {},
+  sendMessageStatus: 'idle'
+};
 
 
-  let zeroPos = 0;
-  board.forEach((row) => {
-    row.forEach((cell) => {
-      if (cell.value === 0) {
-        zeroPos += 1;
-      }
-    })
-  })
-  if (zeroPos === 0) {
-    return;
-  }
+async function fetchOpenAICompletion(messages: Message[]) {
+  const apiMessages = messages.map(m => ({
+    role: m.role,
+    content: m.content
+  }));
 
-  let curr = 0;
-  const maxAddedValues = zeroPos < 2 ? 1 : (zeroPos / 2) | 0;
-  while (curr < maxAddedValues) {
-    randomRowIndex = Math.floor(Math.random() * board.length)
-    randomColIndex = Math.floor(Math.random() * board.length)
-    if (board[randomRowIndex][randomColIndex].value === 0)
-    {
-      board[randomRowIndex][randomColIndex].value = 2;
-      curr++;
-    }
-  }
-}
+  try {
+    const response = await fetch("https://api.openai.com/v1/chat/completions", {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        "Authorization": `Bearer ${OPENAI_API_KEY}`
+      },
+      body: JSON.stringify({
+        model: "gpt-3.5-turbo", // or gpt-4
+        messages: apiMessages,
+      })
+    });
 
-function gameDispatch(game: Game, gameAction: GameAction): Game {
-  switch(gameAction.type) {
-    case "move": {
-      const newBoard = handleMove(game.board, gameAction.command);
-      addNewItemsToTheBoard(newBoard);
-      return  {
-        ...game,
-        board: newBoard,
-      }
+    if (!response.ok) {
+      throw new Error(`API Error: ${response.statusText}`);
     }
-    case "calculate": {
-      return  {
-        ...game,
-      }
-    }
+
+    const data = await response.json();
+    return data.choices[0].message.content;
+  } catch (error) {
+    console.error(error);
+    throw error;
   }
 }
 
 function Current() {
-  const [game, dispatch] = useReducer(gameDispatch, null, initializeGame);
+  const [state, dispatch] = useReducer(mainPageDispatch, initialMainPage);
+  const [inputValue, setInputValue] = useState("");
+  const messagesEndRef = useRef<HTMLDivElement>(null);
 
+  // Auto-scroll to bottom when messages change
   useEffect(() => {
-    window.addEventListener("keyup", (e) => {
-      switch(e.key) {
-        case "ArrowDown": {
-          dispatch({ type: "move", command: "d" });
-          return;
-        }
-        case "ArrowUp": {
-          dispatch({ type: "move", command: "u" });
-          return;
-        }
-        case "ArrowRight": {
-          dispatch({ type: "move", command: "r" });
-          return;
-        }
-        case "ArrowLeft": {
-          dispatch({ type: "move", command: "l" });
-          return;
-        }
-        default:
-          return;
-      }
-    })
+    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+  }, [state.currentMessages]);
+
+  const handleSendMessage = async () => {
+    if (!inputValue.trim()) return;
+
+    // 1. Determine if this is a new chat or existing
+    const isNewChat = !state.activeChatId;
+    const currentChatId = state.activeChatId || crypto.randomUUID(); // Generate ID for new chat
+
+    const textToSend = inputValue;
+    setInputValue(""); // Clear input immediately
+
+    // 2. Dispatch User Message (Optimistic UI)
+    dispatch({ 
+      type: 'user_message_sent', 
+      payload: { content: textToSend, newChatId: isNewChat ? currentChatId : undefined } 
+    });
+
+    try {
+      // 3. Prepare context (include previous messages + new one)
+      // Note: We reconstruct the array here because state update is async and might not be ready
+      const contextMessages: Message[] = [
+        ...(isNewChat ? [] : state.currentMessages),
+        { id: 'temp', role: 'user', content: textToSend, createdAt: Date.now() }
+      ];
+
+      // 4. Call API
+      const aiResponse = await fetchOpenAICompletion(contextMessages);
+
+      // 5. Dispatch Success
+      dispatch({ type: 'api_response_received', payload: { content: aiResponse } });
+
+    } catch (error) {
+      dispatch({ type: 'api_error', payload: { error: 'Failed to fetch' } });
+      alert("Failed to connect to OpenAI. Check API Key.");
+    }
+  };
+
+  const handleKeyDown = (e: React.KeyboardEvent) => {
+    if (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault();
+      handleSendMessage();
+    }
+  };
 
-  }, [])
   return (
-    <div style={gameStyle.container}>
-      <h1> 2048 </h1>
-      <div style={gameStyle.board}>
-        {game.board.map((row: Cell[]) => {
-          return row.map((cell: Cell) => (<div style={gameStyle.cell(cell.color)}> {cell.value} </div>))
-        })}
+    <div style={STYLES.mainContainer}>
+      {/* Sidebar */}
+      <div style={STYLES.sideBar}>
+        <h3 style={{color: '#ececf1', marginBottom: '20px'}}>History</h3>
+        <div 
+          style={{...STYLES.sideBarItem, border: '1px dashed #555'}}
+          onClick={() => {
+             // Reset to empty view for a new chat
+             // We can achieve this by setting activeChatId to null locally if we wanted
+             // But for this simple reducer, we can just reload the page or add a 'reset' action.
+             // For now, let's just allow clicking existing ones.
+             window.location.reload(); 
+          }}
+        >
+          + New Chat
+        </div>
+
+        {state.chatHistory.map((hist) => (
+          <div 
+            key={hist.id} 
+            style={{
+              ...STYLES.sideBarItem,
+              backgroundColor: state.activeChatId === hist.id ? '#555' : '#333'
+            }}
+            onClick={() => dispatch({ type: 'select_chat', payload: { chatId: hist.id } })}
+          > 
+            {hist.title} 
+          </div>
+        ))}
+      </div>
+
+      {/* Main Chat Area */}
+      <div style={STYLES.mainChat}>
+        <div style={STYLES.mainMessage}>
+          {state.currentMessages.length === 0 && (
+            <div style={{color: '#666', textAlign: 'center', marginTop: '40%'}}>
+              Send a message to start...
+            </div>
+          )}
+
+          {state.currentMessages.map((msg) => (
+            <div 
+              key={msg.id} 
+              style={{
+                ...STYLES.messageBubble,
+                backgroundColor: msg.role === 'assistant' ? '#444654' : 'transparent'
+              }}
+            >
+              <strong style={{color: msg.role === 'assistant' ? '#10a37f' : '#ececf1'}}>
+                {msg.role === 'assistant' ? 'AI' : 'You'}:
+              </strong>
+              <div style={{whiteSpace: 'pre-wrap', marginTop: '5px'}}>{msg.content}</div>
+            </div>
+          ))}
+          
+          {state.sendMessageStatus === 'inProgress' && (
+             <div style={{...STYLES.messageBubble, color: '#888'}}>AI is typing...</div>
+          )}
+          <div ref={messagesEndRef} />
+        </div>
+
+        {/* Input Area */}
+        <textarea 
+          style={STYLES.inputBar}
+          placeholder="Send a message..."
+          value={inputValue}
+          onChange={(e) => setInputValue(e.target.value)}
+          onKeyDown={handleKeyDown}
+          disabled={state.sendMessageStatus === 'inProgress'}
+        />
       </div>
     </div>
   );
 }
 
 ReactDOM.createRoot(document.getElementById("root")!).render(<Current />);
+
+
+https://www.linkedin.com/in/drakewong/
+https://www.linkedin.com/in/dmitry-manannikov/