Mercurial
changeset 47:829623189a57
[Gara] Android commit. Bazelfied it.
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_round.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_round.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 + + + +- 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
--- /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>
--- /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/