# HG changeset patch # User June Park # Date 1766256961 18000 # Node ID d64a8c189a77a4f1d30e561002a81df25e2bc373 # Parent 983769fba767dbae6672d085ab681eaf677317d2# Parent e06bc03d9618281e6f4b028da9d8aeb0ac1096e4 Merged diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,3 @@ +# Asyncio + +Wanted to know how asycio works in python so made few questiosn that I would probably ask others to show how to do use these. diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/bank_question/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/bank_question/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,34 @@ +# Bank Account + +You are given a simplified multi-threaded “bank account” implementation used by one of our infrastructure teams. The current code processes deposits concurrently but produces incorrect results. + +``` +class BankAccount: + def __init__(self, balance): + self.balance = balance + + def deposit(self, amount): + new_balance = self.balance + amount # read + time.sleep(0.1) # simulate delay + self.balance = new_balance # write + +account = BankAccount(0) + +with ThreadPoolExecutor(max_workers=2) as executor: + futures = [ + executor.submit(account.deposit, 500), + executor.submit(account.deposit, 700), + ] +``` + +Task + +Identify the concurrency bugs in the implementation. + +Explain why these bugs occur. + +Fix the implementation without modifying the existing BankAccount class directly. + +You may use subclassing (Python) or embedding (Go-style composition), depending on the language you choose. + +Ensure your solution is thread-safe and produces the correct final balance (1200). diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/bank_question/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/bank_question/main.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,43 @@ +# # Bank Account +# +# You are given a simplified multi-threaded “bank account” implementation used by one of our infrastructure teams. The current code processes deposits concurrently but produces incorrect results. +# +# Task +# +# Identify the concurrency bugs in the implementation. +# +# Explain why these bugs occur. +# +# Fix the implementation without modifying the existing BankAccount class directly. +# +# You may use subclassing (Python) or embedding (Go-style composition), depending on the language you choose. +# +# Ensure your solution is thread-safe and produces the correct final balance (1200). + +from threading import Lock +from concurrent.futures import ThreadPoolExecutor +import time + +class BankAccount: + def __init__(self, balance): + self.balance = balance + self._lock = Lock() + + def deposit(self, amount): + while (self._lock.locked()): + pass + self._lock.acquire() + new_balance = self.balance + amount # read + time.sleep(0.1) # simulate delay + self.balance = new_balance # write + self._lock.release() + + +account = BankAccount(0) + +with ThreadPoolExecutor(max_workers=2) as executor: + futures = [ + executor.submit(account.deposit, 500), + executor.submit(account.deposit, 700), + ] +print(account.balance) diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/bucket_questions/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/bucket_questions/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,14 @@ +# Bucket questions + +## Context + +Please implement two functions: refillTokenBucket and useTokens. Two classes are already defined: DistributedCache and TokenBucket. + +refillTokenBucket(user_id): Refill tokens for the specified user's bucket. + +useTokens(user_id, tokens): Check if there are enough tokens in the specified user's token bucket. If so, update the remaining token count and return true; otherwise, return false. You are required to use instances of the existing classes to implement these functions. + +## Requirements + +Use the API provided by DistributedCache to get and update the user's TokenBucket. +Implement the functionality using existing class instances and ensure the behavior is correct across multiple calls diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/bucket_questions/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/bucket_questions/main.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,65 @@ +import asyncio +from concurrent.futures import ThreadPoolExecutor +from typing import Dict + +class DistributedCache: + + def __init__(self): + self._user_bucket_map: Dict[str, TokenBucket] = {} + + + def get_bucket(self, user_id: str): + return self._user_bucket_map[user_id] + + def set_bucket(self, user_id: str): + self._user_bucket_map[user_id] = TokenBucket() + + +INITIAL_VALUES = 10 + +class TokenBucket: + + def __init__(self, initial_values = INITIAL_VALUES): + self._tokens = initial_values + self._refill_values = initial_values + self._lock = asyncio.Lock() + + def get_tokens(self): + print(self._tokens) + return self._tokens + + async def consume_tokens(self, token: int): + async with self._lock: + if self.get_tokens() < token: + return False + + await asyncio.sleep(1) + self._tokens -= token + return True + + async def refill_tokens(self): + async with self._lock: + self._tokens = self._refill_values + + +cache = DistributedCache() + +user_1 = "JUNE" +user_2 = "VICTOR" +cache.set_bucket(user_1) +cache.set_bucket(user_2) + +async def refill_token_bucket(user_id: str): + await cache.get_bucket(user_id).refill_tokens() + +async def use_tokens(user_id: str, tokens: int): + return await cache.get_bucket(user_id).consume_tokens(tokens) + +def run(): + with ThreadPoolExecutor(max_workers=3) as thread: + thread.submit(asyncio.run, use_tokens(user_1, 10)) + thread.submit(asyncio.run, use_tokens(user_1, 10)) + thread.submit(asyncio.run, use_tokens(user_2, 10)) + +if __name__ == "__main__": + run() diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/bucket_questions/single_thread_async.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/bucket_questions/single_thread_async.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,67 @@ +import asyncio +from typing import Dict + +class DistributedCache: + + def __init__(self): + self._user_bucket_map: Dict[str, TokenBucket] = {} + + + def get_bucket(self, user_id: str): + return self._user_bucket_map[user_id] + + def set_bucket(self, user_id: str): + self._user_bucket_map[user_id] = TokenBucket() + + +INITIAL_VALUES = 10 + +class TokenBucket: + + def __init__(self, initial_values = INITIAL_VALUES): + self._tokens = initial_values + self._refill_values = initial_values + self._lock = asyncio.Lock() + + def get_tokens(self): + print(self._tokens) + return self._tokens + + async def consume_tokens(self, token: int): + async with self._lock: + if self.get_tokens() < token: + return False + + await asyncio.sleep(1) + self._tokens -= token + return True + + async def refill_tokens(self): + async with self._lock: + self._tokens = self._refill_values + + +cache = DistributedCache() + +user_1 = "JUNE" +user_2 = "VICTOR" +cache.set_bucket(user_1) +cache.set_bucket(user_2) + +async def refill_token_bucket(user_id: str): + await cache.get_bucket(user_id).refill_tokens() + +async def use_tokens(user_id: str, tokens: int): + return await cache.get_bucket(user_id).consume_tokens(tokens) + +async def run(): + return_value = await asyncio.gather( + use_tokens(user_1, 10), + refill_token_bucket(user_1), + use_tokens(user_1, 10), + use_tokens(user_2, 10), + ) + print(return_value) + +if __name__ == "__main__": + asyncio.run(run()) diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/database/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/database/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,19 @@ +# Design an in-memory key-value database that supports the following commands and fully handles nested transactions. + +## Core Operations: +SET : Sets the value for a key. + +GET : Returns the value for a key. + +UNSET : Removes the key and its value. + +## Transaction Operations: +BEGIN: Starts a new transaction scope. If a transaction is already active, this starts a nested transaction. + +COMMIT: Applies all changes made in the current transaction scope and all its active nested transactions to the parent scope (or to the main database state if no parent exists). After a successful commit, the transaction scope is closed. + + ROLLBACK: Discards all changes made in the current transaction scope and all its active nested transactions, restoring the state to what it was when the BEGIN command was issued for the current scope. After a successful rollback, the transaction scope is closed. + +## Implementation Goal: + +Design the primary data structures and outline the logic for SET, COMMIT, and ROLLBACK to ensure nested transactions operate correctly. Explain how the state of the database is managed across multiple transaction layers. diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/database/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/database/main.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,16 @@ +class Database: + + def __init__(self): + self.db = {} + self.state = 'idle' + + def set(self, key, value): + self.db[key] = value + + def get(self, key): + return self.db[key] + + def unset(self, key): + del self.db[key] + + diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/frontend/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/frontend/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,13 @@ +Frontend Task + +1. Component Implementation: Enhanced TypeAhead (Good?) + +Develop a TypeAhead input component that filters a dataset based on user entry. Implement a debounce mechanism to optimize performance by limiting the frequency of function execution during typing. Apply text processing logic to identify and visually highlight the specific substrings within the results that match the user's input. + +2. Algorithmic Challenge: Increasing Subsequence(Okay?) + +Analyze a given array of integers and write a function to determine the length of the longest subsequent growing sequence contained within the array. + +3. Frontend Architecture: AI Prompt and Context Interface + +Construct a dual-pane user interface featuring a prompt history sidebar on the left and a main content area on the right for input and context display. Implement data fetching from the OpenAI API. Develop state management logic where submitting a new prompt triggers an API call, and selecting a specific prompt from the history injects the corresponding past response to serve as the context for the subsequent request. diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/frontend/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/frontend/index.html Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,7 @@ + + + +
+ + + diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/frontend/longest_subinteger.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/frontend/longest_subinteger.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,53 @@ +# Analyze a given array of integers and write a function to determine the length of the longest subsequent growing sequence contained within the array. + + +# 1,2,3,4,2,1,3 +# | + +# Example 1: +# +# Input: nums = [10,9,2,5,3,7,101,18] +# [(2, 1), 3, 5, 7, 9, 10, 18, 101] +# | +# Output: 4 + +# Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. + +# Example 2: +# +# Input: nums = [0,1,0,3,2,3] +# Output: 4 +# Example 3: +# +# Input: nums = [7,7,7,7,7,7,7] +# Output: 1 + + +# Longest Increasing Subsequence +def main(nums): + ans = 0 + cache = set() + + def dfs(val, pos, curr_ans): + nonlocal ans + + if ((val, pos, curr_ans) in cache): + return + + if pos > len(nums): + return + if nums[pos] > val: + curr_ans += 1 + else: + ans = max(ans, curr_ans) + return + for i in range(pos, len(nums)): + dfs(nums[pos], i, curr_ans) + + for i in range(len(nums)): + dfs(float("-inf"), i, 0) + + return ans + + +print(main([10,9,2,5,3,7,101,18])) diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/frontend/main.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/frontend/main.js Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,135 @@ +// Develop a TypeAhead input component that filters a dataset based on user entry. Implement a debounce mechanism to optimize performance by limiting the frequency of function execution during typing. Apply text processing logic to identify and visually highlight the specific substrings within the results that match the user's input. +// + + +// 3. text processing logic which ones... + + +let previousId = 0; +const inputEle = document.getElementById("search"); +const divSuggestion = document.getElementById("suggestion"); +const tries = {} +const userNames = [ + "Ephemeral", + "Mellifluous", + "Ubiquitous", + "Voracious", + "Serendipity", + "Quixotic", + "Languid", + "Ephemeral", + "Susurrus", + "Ebullient", + "Petrichor", + "Ineluctable", + "Platitude", + "Ennui", + "Axiom", + "Cacophony", + "Halcyon", + "Ineffable", + "Juxtapose", + "Lugubrious", + "Nefarious", + "Ostracize", + "Pernicious", + "Ruminate", + "Solipsism", + "Taciturn", + "Wanderlust", + "Zephyr", + "Discombobulate", + "Esoteric", + "Incendiary", + "Kudos", + "Misanthrope", + "Pensive", + "Quagmire", + "Ramify", + "Stymie", + "Unctuous", + "Vex", + "Wistful", + "Banal", + "Dichotomy", + "Fecund", + "Garrulous", + "Hegemony", + "Imbroglio", + "Knell", + "Mirth", + "Obfuscate", + "Parsimonious", +] + +function createTries(word) { + curr = tries + for (let letter of word.toLowerCase()) { + if (!curr[letter]) { + curr[letter] = {}; + } + if (!curr[letter]["#"]) { + curr[letter]["#"] = [] + curr[letter]["#"].push(word) + } else { + curr[letter]["#"].push(word) + } + + curr = curr[letter]; + } +} + +userNames.forEach((word) => createTries(word)); + +function findWord(word) { + const results = new Set() + + function dfs(currTries, currWordPosition, fuzz_search, curr_sequential, max_sequential = 0) { + if (currWordPosition === word.length || fuzz_search > 1) { + console.log(currTries, currWordPosition, fuzz_search, curr_sequential, max_sequential) + if (max_sequential >= 2) { + currTries['#'].forEach((word) => results.add(word)); + console.log("hello") + } + return; + } + letter = word[currWordPosition].toLowerCase(); + Object.keys(currTries).forEach((key) => { + if (key === '#') return; + dfs(currTries[key], + currWordPosition + 1, + (key != letter) ? fuzz_search + 1 : fuzz_search, + (key == letter) ? curr_sequential + 1 : 0, + Math.max((key == letter) ? curr_sequential + 1 : 0, max_sequential) + ); + }) + } + + dfs(tries, 0, 0, 0) + return results; +} +function populateSuggestions(inputValue) { + const words = findWord(inputValue); + + divSuggestion.innerHTML = ""; + for (let word of words) { + let res = ""; + const lowerInput = inputValue.toLowerCase(); + for (let letter of word) { + if (lowerInput.includes(letter.toLowerCase())) { + res += `${letter}`; + } else { + res += letter; + } + } + divSuggestion.innerHTML += res + "
"; + } +} + +let i = 0 +inputEle.addEventListener('keypress', () => { + if (previousId) { + clearTimeout(previousId); + } + previousId = setTimeout(() => populateSuggestions(inputEle.value), 500); // 1, 2, 3 +}) diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/inference/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/inference/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,30 @@ +Inference Questions + +Context + +You are tasked with building a simplified inference engine component responsible for handling incoming user requests for a large language model (LLM). To optimize throughput and GPU utilization, the engine must batch multiple requests together, run the inference call once per batch, and then deconstruct the results to return token-level output to the individual users. + +Objective + +Complete the provided Python class, BatchInferenceEngine by implementing the methods necessary to: +Queue incoming user requests. +Process a batch when the queue reaches a defined batch size. + +Simulate the token-level output from an LLM and correctly associate each generated token with its original request. + + +Task Requirements + + + + + +Implement the logic for $enqueue\_request$. + + + +Implement the logic for $\_process\_batch$. + + + +Demonstrate the usage by creating 7 unique requests and enqueueing them one by one. Show the state of the queue and the processed tokens after each batch run. diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/inference/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/inference/main.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,107 @@ +from typing import List, Dict, Any, Optional + +class UserRequest: + """Represents an incoming user request.""" + def __init__(self, request_id: int, prompt: str): + self.request_id = request_id + self.prompt = prompt + self.output_tokens: List[str] = [] # Stores tokens as they are generated + + def __repr__(self): + return f"Request(ID={self.request_id}, Prompt='{self.prompt[:20]}...', Tokens={len(self.output_tokens)})" + +# --- Mock LLM Interface --- +def mock_inference_call(prompts: List[str], batch_id: int) -> Dict[str, Any]: + """ + Simulates the call to the underlying LLM/GPU. + + In a real scenario, this returns generated tokens and associated metadata. + For this mock, we return a flat list of tokens, one for each request + in the batch, and the batch ID for verification. + + The length of the tokens list MUST equal the length of the prompts list. + """ + print(f" [INFERENCE] Running Batch {batch_id} with {len(prompts)} requests...") + results = { + 'batch_id': batch_id, + # Simulate generating a single new token for each request in the batch + 'generated_tokens': [ + f"token_{i+1}_of_{batch_id}" for i, _ in enumerate(prompts) + ] + } + return results +# ------------------------- + + +class BatchInferenceEngine: + """ + A simplified inference engine that handles request batching and + token-level result distribution. + """ + def __init__(self, batch_size: int = 4): + self.batch_size = batch_size + self.request_queue: List[UserRequest] = [] + self.next_batch_id = 1 + + def enqueue_request(self, request: UserRequest) -> None: + """ + Adds a new request to the queue and triggers batch processing if + the batch size is reached. + """ + # --- YOUR CODE HERE --- + # 1. Add the request to the queue. + # 2. Check if the queue size meets or exceeds self.batch_size. + # 3. If so, call self._process_batch(). + # ---------------------- + self.request_queue.append(request) + while len(self.request_queue) > self.batch_size: + self._process_batch() + + + def _process_batch(self) -> None: + """ + Executes the inference call for the current batch and distributes + the results back to the individual requests. + """ + batch = self.request_queue[:self.batch_size] + prompts = map(lambda x : x.prompt, batch) + results = mock_inference_call(list(prompts), self.next_batch_id) + for request in self.request_queue: + request.output_tokens = results["generated_tokens"] + self.request_queue[self.batch_size:] + self.next_batch_id += 1 + + def get_results(self, request_id: int) -> Optional[List[str]]: + """ + In a real system, this would retrieve results from a separate + completed-requests store. For this mock, assume we can only + retrieve results for requests that have been fully processed and + are no longer in the queue. + """ + # For simplicity, assume all requests that have been processed + # by a batch call have completed their generation for this *single step* + # of the mock. If you want to make this more realistic, feel free to + # expand the UserRequest class to include a 'is_complete' flag. + + # For the provided mock structure, we'll just check the queue: + + for req in self.request_queue: + if req.request_id == request_id: + # If it's still in the queue, it hasn't been processed yet + return None + + # In a complete system, you'd look up the request ID in a completed-requests map. + # For this simplified version, let's just return a simulated result for + # requests that *would* have been processed: + + # If the request ID is less than the ID of the requests that would be + # processed in the *next* batch, we simulate a complete token output. + if request_id < self.next_batch_id * self.batch_size: + # Simple simulation: + return [f"token_X_of_{batch_num}" for batch_num in range(1, self.next_batch_id)] + + return None + + +def main(): + pass diff -r e06bc03d9618 -r d64a8c189a77 asyncio_threads/stop_token/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/asyncio_threads/stop_token/main.py Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,42 @@ +from typing import Generator +from typing import List + +# [Hello There, "I am Something"] +# | + +def truncate_stream(text_stream: Generator[str, None, None], stop_token: str) -> Generator[str, None, None]: + buffer = "" + for chunk in text_stream: + can_be_prefix = False + buffer += chunk + if stop_token not in buffer: + for end in range(1, len(stop_token)): + if stop_token[:end] in buffer[-1 * len(stop_token):]: + can_be_prefix = True + if not can_be_prefix: + yield chunk + buffer = "" + else: + pos = buffer.find(stop_token) + yield buffer[:pos] + return + +def stream_to_list(stream: Generator[str, None, None]) -> List[str]: + return [chunk for chunk in stream] + +def list_to_stream(chunks: List[str]) -> Generator[str, None, None]: + for chunk in chunks: + yield chunk + + +print( + stream_to_list( + truncate_stream(list_to_stream(["Hello there ", "I'm doing great today.", "Thanks for asking.", "How are you"]), "") + ) +) + +print( + stream_to_list( + truncate_stream(list_to_stream(["Hello there ", "I'm doing great today.", "Thanks for asking.", "How are you"]), "") + ) +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/.bazelrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/.bazelrc Sat Dec 20 13:56:01 2025 -0500 @@ -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 diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/.bazelversion --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/.bazelversion Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,1 @@ +7.2.0rc1 diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/BUILD --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/BUILD Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,7 @@ +platform( + name = "arm64-v8a", + constraint_values = [ + "@platforms//cpu:arm64", + "@platforms//os:android", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -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 + + + + +``` + +## 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"],`. diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/WORKSPACE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/WORKSPACE Sat Dec 20 13:56:01 2025 -0500 @@ -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", +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/BUILD --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/app/BUILD Sat Dec 20 13:56:01 2025 -0500 @@ -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", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/google-services.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/app/google-services.json Sat Dec 20 13:56:01 2025 -0500 @@ -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" +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/AndroidManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/firebase-cloud-messaging/app/src/main/AndroidManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MainActivity.java --- /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 20 13:56:01 2025 -0500 @@ -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()); + } +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseInstanceIdService.java --- /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 20 13:56:01 2025 -0500 @@ -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. + } + +} + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/java/com/example/myapplication/MyFirebaseMessagingService.java --- /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 20 13:56:01 2025 -0500 @@ -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. + } + +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/drawable-v24/ic_launcher_foreground.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,34 @@ + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/drawable/ic_launcher_background.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/layout/activity_main.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,18 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary file gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher_round.png Binary file gara/android/firebase-cloud-messaging/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/values/colors.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/values/strings.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,3 @@ + + My Application + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/firebase-cloud-messaging/app/src/main/res/values/styles.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,11 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/.bazelversion --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/.bazelversion Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,1 @@ +6.5.0 diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/MODULE.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/MODULE.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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") diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -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` diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/WORKSPACE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/WORKSPACE Sat Dec 20 13:56:01 2025 -0500 @@ -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() diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/AndroidManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/AndroidManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,7 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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"], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/LibraryManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/LibraryManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/java/com/example/android/bazel/MainActivity.kt --- /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 20 13:56:01 2025 -0500 @@ -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 + ) + } +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/drawable-v24/ic_launcher_foreground.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,34 @@ + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/drawable/ic_launcher_background.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-hdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png Binary file gara/android/jetpack-compose/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/values/colors.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/res/values/colors.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/values/strings.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/res/values/strings.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,3 @@ + + Built By Bazel + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/jetpack-compose/app/src/main/res/values/styles.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/jetpack-compose/app/src/main/res/values/styles.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,11 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/.bazelrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/.bazelrc Sat Dec 20 13:56:01 2025 -0500 @@ -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 diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/.bazelversion --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/.bazelversion Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,2 @@ +7.3.1 + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/MODULE.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/MODULE.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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") diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -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` + + + +## 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. diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/WORKSPACE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/WORKSPACE Sat Dec 20 13:56:01 2025 -0500 @@ -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") diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/AndroidManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/AndroidManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,8 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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"], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/LibraryManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/LibraryManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/cpp/native-lib.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/cpp/native-lib.cpp Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,13 @@ +#include +#include + +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()); +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/java/com/example/android/bazel/MainActivity.java --- /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 20 13:56:01 2025 -0500 @@ -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(); +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/drawable-v24/ic_launcher_foreground.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,34 @@ + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/drawable/ic_launcher_background.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/layout/activity_main.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/res/layout/activity_main.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,21 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary file gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher_round.png Binary file gara/android/ndk/app/src/main/res/mipmap-hdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary file gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher_round.png Binary file gara/android/ndk/app/src/main/res/mipmap-mdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary file gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png Binary file gara/android/ndk/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary file gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png Binary file gara/android/ndk/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary file gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png Binary file gara/android/ndk/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/values/colors.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/res/values/colors.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/values/strings.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/res/values/strings.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,3 @@ + + Built By Bazel + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/app/src/main/res/values/styles.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/ndk/app/src/main/res/values/styles.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,11 @@ + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/images/graph.png Binary file gara/android/ndk/images/graph.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/ndk/images/result.png Binary file gara/android/ndk/images/result.png has changed diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,7 @@ +platform( + name = "arm64-v8a", + constraint_values = [ + "@platforms//cpu:arm64", + "@platforms//os:android", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/MODULE.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/MODULE.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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") diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/README.md Sat Dec 20 13:56:01 2025 -0500 @@ -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` diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/WORKSPACE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/WORKSPACE Sat Dec 20 13:56:01 2025 -0500 @@ -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() diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/BUILD.bazel --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/app/BUILD.bazel Sat Dec 20 13:56:01 2025 -0500 @@ -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", + ], +) diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/AndroidManifest.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gara/android/robolectric-testing/app/src/main/AndroidManifest.xml Sat Dec 20 13:56:01 2025 -0500 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/LoginActivity.kt --- /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 20 13:56:01 2025 -0500 @@ -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) + } +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/java/com/example/android/bazel/WelcomeActivity.kt --- /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 20 13:56:01 2025 -0500 @@ -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)) + }) + } +} diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/res/drawable-v24/ic_launcher_foreground.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,34 @@ + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/res/drawable/ic_launcher_background.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r e06bc03d9618 -r d64a8c189a77 gara/android/robolectric-testing/app/src/main/res/layout/welcome_activity.xml --- /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 20 13:56:01 2025 -0500 @@ -0,0 +1,13 @@ + + + +