### First-time Setup with Model Selection (Data Flow)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Illustrates the data flow for a first-time user setup in BotDrop Android. It covers user interaction from opening the app, selecting a provider and model, authentication, saving configuration, and navigating to the channel setup.
```text
User opens app
↓
SetupActivity → AuthFragment
↓
User selects Provider (Google)
↓
ModelSelectorDialog.show()
↓
Execute: openclaw models list
↓
Parse and display models
↓
User searches "gemini" → Filter results
↓
User selects "google/gemini-3-flash-preview"
↓
Callback: onModelSelected("google", "gemini-3-flash-preview")
↓
AuthFragment continues with auth method selection
↓
User enters API key
↓
Verify success
↓
ConfigTemplate template = new ConfigTemplate()
template.provider = "google"
template.model = "gemini-3-flash-preview"
template.apiKey = "AIza..."
ConfigTemplateCache.saveTemplate(context, template)
↓
BotDropConfig.setProvider("google", "gemini-3-flash-preview")
BotDropConfig.setApiKey("google", "AIza...")
↓
Navigate to ChannelFragment
```
--------------------------------
### Setup and Initialization of uiautomator2
Source: https://github.com/zhixianio/botdrop-android/blob/master/app/src/main/assets/skills/botdrop-automation/SKILL.md
Commands to install dependencies, verify the service connection, and initialize the HTTPDevice client for remote mobile control.
```bash
python -c "import uiautomator2" 2>/dev/null || apt install -y python-uiautomator2-botdrop
curl -sS http://127.0.0.1:9008/ping
```
```python
import uiautomator2 as u2
# Initialize device
d = u2.HTTPDevice("http://127.0.0.1:9008")
# Verify connection
print(d.app_current())
print(d.info["currentPackageName"])
```
--------------------------------
### Add Manual Update Button to Setup UI (XML)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
This snippet shows the XML layout modification in `activity_botdrop_setup.xml` to include a new ImageButton for manually checking updates. It's placed in the navigation bar, visible across all setup steps.
```xml
```
--------------------------------
### Install and Verify OpenClaw
Source: https://context7.com/zhixianio/botdrop-android/llms.txt
Handles the installation process of OpenClaw with progress tracking callbacks and provides static utility methods to check installation status and version.
```java
mService.installOpenclaw(new BotDropService.InstallProgressCallback() {
@Override
public void onStepStart(int step, String message) {
runOnUiThread(() -> {
progressBar.setProgress(step * 33);
statusText.setText(message);
});
}
@Override
public void onStepComplete(int step) {
Log.d(TAG, "Step " + step + " completed");
}
@Override
public void onError(String error) {
runOnUiThread(() -> {
showErrorDialog("Installation failed", error);
});
}
@Override
public void onComplete() {
runOnUiThread(() -> {
navigateToNextStep();
});
}
});
if (BotDropService.isOpenclawInstalled()) {
String version = BotDropService.getOpenclawVersion();
Log.i(TAG, "OpenClaw version: " + version);
}
```
--------------------------------
### Verify On-Device Files and Logs (ADB/Bash)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-03-17-openclaw-offline-bundle.md
These commands verify the presence of staged manifest and runtime archives on the device and check installation logs for offline extraction. The `run-as` command allows executing commands within the app's sandbox, while `logcat` filters for relevant installation and plugin deployment messages. Expected output includes confirmation of offline asset extraction and QQ plugin deployment.
```bash
adb shell run-as app.botdrop ls -R files/usr/share/botdrop/offline-openclaw
adb shell logcat -d -v time | grep -E 'BundledOpenclaw|BotDrop\.TermuxInstaller|BotDrop\.BotDropService|qqbot'
```
--------------------------------
### Starting a UserService
Source: https://github.com/zhixianio/botdrop-android/blob/master/api/README.md
Initiates a UserService using the `bindUserService` method. This method requires `UserServiceArgs` to specify the service and options, and a `ServiceConnection` to handle connection events. The service class must implement `IBinder` and typically extends `IYouAidlInterface.Stub`.
```java
bindUserService(userServiceArgs, serviceConnection);
// Example Service Implementation:
public class YourService extends IYouAidlInterface.Stub {
// Constructor with Context (available from Shizuku v13)
public YourService(Context context) {
// ...
}
// Default constructor (used by older Shizuku)
public YourService() {
// ...
}
// Implement AIDL methods here
}
```
--------------------------------
### Build Android App with Bundled Inputs (Gradle/Bash)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-03-17-openclaw-offline-bundle.md
This command builds the Android application in debug mode, including bundled offline assets and installs it on a connected device. It requires specifying the paths to the OpenCLAW runtime and QQBot plugin tarball archives. The expected outcome is a successfully installed APK containing the generated offline assets.
```bash
BOTDROP_OPENCLAW_BUNDLE_TGZ=/abs/path/to/openclaw-runtime.tar.gz \
BOTDROP_QQBOT_PLUGIN_TGZ=/abs/path/to/sliverp-qqbot-1.5.4.tgz \
./gradlew :app:assembleDebug :app:installDebug --no-daemon
```
--------------------------------
### Configure Java Development Environment
Source: https://github.com/zhixianio/botdrop-android/blob/master/CLAUDE.md
Sets the JAVA_HOME environment variable to point to the required OpenJDK 17 installation. This is essential for consistent Gradle builds and resolving 'Unable to locate a Java Runtime' errors.
```bash
export JAVA_HOME=/opt/homebrew/opt/openjdk@17
export PATH="$JAVA_HOME/bin:$PATH"
```
--------------------------------
### UI Interaction Patterns
Source: https://github.com/zhixianio/botdrop-android/blob/master/app/src/main/assets/skills/botdrop-automation/SKILL.md
Examples of using XPath and selector facades to interact with UI elements. XPath is recommended for dynamic and complex application structures.
```python
# XPath interaction (preferred)
if d.xpath('//*[@text="Settings"]').exists:
d.xpath('//*[@text="Settings"]').click()
# Selector facade interaction
d(text="Settings")[0].click()
```
--------------------------------
### Execute Gradle Build and Test Commands
Source: https://github.com/zhixianio/botdrop-android/blob/master/CLAUDE.md
Standard commands for building debug/release APKs, running unit tests, and cleaning the project build directory. Requires the JAVA_HOME environment variable to be set prior to execution.
```bash
./gradlew clean assembleDebug
./gradlew :app:testDebugUnitTest
./gradlew clean
./gradlew assembleRelease
./gradlew installDebug
```
--------------------------------
### Get UI Tree
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/design-android-automation.md
Retrieves a compact tree representation of the current foreground window's accessibility nodes. This allows inspection of the UI structure.
```APIDOC
## GET /ui/tree
### Description
Retrieves a compact tree of nodes from the current foreground window's accessibility node tree. Each node includes details like nodeId, package, class, text, content description, resource ID, bounds, and state information.
### Method
GET
### Endpoint
/ui/tree
### Parameters
None
### Request Example
None
### Response
#### Success Response (200)
- **nodes** (array) - An array of node objects representing the UI tree.
- **nodeId** (string) - Unique identifier for the node.
- **package** (string) - The package name of the application owning the node.
- **class** (string) - The class name of the UI element.
- **text** (string) - The text content of the node.
- **contentDesc** (string) - The content description of the node.
- **resourceId** (string) - The resource ID of the node.
- **bounds** (object) - The bounding box of the node (e.g., {x1, y1, x2, y2}).
- **clickable** (boolean) - Whether the node is clickable.
- **enabled** (boolean) - Whether the node is enabled.
- **visible** (boolean) - Whether the node is visible.
- **children** (array) - An array of child node objects.
#### Response Example
```json
{
"nodes": [
{
"nodeId": "1",
"package": "com.example.app",
"class": "android.widget.LinearLayout",
"text": null,
"contentDesc": null,
"resourceId": null,
"bounds": {"x1": 0, "y1": 0, "x2": 1080, "y2": 1920},
"clickable": false,
"enabled": true,
"visible": true,
"children": [
{
"nodeId": "2",
"package": "com.example.app",
"class": "android.widget.TextView",
"text": "Hello World",
"contentDesc": null,
"resourceId": "com.example.app:id/textView",
"bounds": {"x1": 100, "y1": 100, "x2": 300, "y2": 200},
"clickable": false,
"enabled": true,
"visible": true,
"children": []
}
]
}
]
}
```
```
--------------------------------
### Integrate Messaging Platforms with ChannelSetupHelper
Source: https://context7.com/zhixianio/botdrop-android/llms.txt
Handles configuration for Telegram, Discord, Feishu, and QQ Bot integrations. It supports decoding setup codes from external bots and provides methods to persist channel-specific credentials and identifiers.
```java
String setupCode = "BOTDROP-tg-eyJ2IjoxLCJwbGF0Zm9ybSI6InRlbGVncmFtIi...";
ChannelSetupHelper.SetupCodeData data = ChannelSetupHelper.decodeSetupCode(setupCode);
if (data != null) {
ChannelSetupHelper.writeChannelConfig(data.platform, data.botToken, data.ownerId);
}
ChannelSetupHelper.writeChannelConfig("telegram", "7123456789:AAF8x...", "987654321");
ChannelSetupHelper.writeChannelConfig("discord", "MTIzNDU2Nzg5MDEyMzQ1Njc4OQ...", "123456789012345678", "999888777666555444", "111222333444555666");
ChannelSetupHelper.writeFeishuChannelConfig("cli_xxx...", "xxxyyy...", "ou_xxx...");
ChannelSetupHelper.writeQQBotChannelConfig("102012345", "xxxyyy...");
if (ChannelSetupHelper.hasAnyChannelConfigured()) {
ChannelSetupHelper.removeChannelConfig("discord");
}
```
--------------------------------
### Manage OpenClaw Gateway Lifecycle
Source: https://context7.com/zhixianio/botdrop-android/llms.txt
Provides methods to start, stop, restart, and monitor the uptime of the OpenClaw background daemon. These operations are performed asynchronously via callback results.
```java
mService.startGateway(result -> {
if (result.success) {
Log.i(TAG, "Gateway started successfully");
} else {
Log.e(TAG, "Failed to start gateway: " + result.stderr);
showError("Gateway start failed: " + result.stderr);
}
});
mService.stopGateway(result -> {
if (result.success) {
Log.i(TAG, "Gateway stopped");
}
});
mService.restartGateway(result -> {
if (result.success) {
Log.i(TAG, "Gateway restarted successfully");
}
});
mService.getGatewayUptime(result -> {
if (result.success) {
String uptime = result.stdout.trim();
updateUptimeDisplay(uptime);
}
});
```
--------------------------------
### Implement Configuration Restore in SetupActivity
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Checks for existing configuration templates on startup and provides an AlertDialog to restore settings. Includes logic to apply the template and navigate the user based on the restored configuration state.
```java
// Check for cached config
if (ConfigTemplateCache.hasTemplate(this)) {
showRestoreConfigDialog();
}
private void showRestoreConfigDialog() {
new androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("Restore Configuration?")
.setMessage("Use your previous configuration to set up quickly.")
.setPositiveButton("Use Previous", (dialog, which) -> {
applyTemplateAndContinue();
})
.setNegativeButton("Start Fresh", (dialog, which) -> {
dialog.dismiss();
})
.setCancelable(false)
.show();
}
private void applyTemplateAndContinue() {
ConfigTemplate template = ConfigTemplateCache.loadTemplate(this);
if (template == null || !template.isValid()) {
Toast.makeText(this, "Failed to load previous config", Toast.LENGTH_SHORT).show();
return;
}
boolean success = ConfigTemplateCache.applyTemplate(this, template);
if (!success) {
Toast.makeText(this, "Failed to apply config", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(this, "Configuration restored", Toast.LENGTH_SHORT).show();
if (template.tgBotToken != null && !template.tgBotToken.isEmpty()) {
finish();
Intent intent = new Intent(this, DashboardActivity.class);
startActivity(intent);
} else {
mViewPager.setCurrentItem(STEP_CHANNEL);
}
}
```
--------------------------------
### Integrate Manual Update Button Click Listener (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
This Java code snippet integrates the manual update button into `SetupActivity`. It sets an `OnClickListener` to trigger `UpdateChecker.forceCheck` and handles the callback to display update information or a 'no updates' message, re-enabling the button afterward.
```java
// SetupActivity.onCreate()
Button checkUpdatesBtn = findViewById(R.id.setup_check_updates);
checkUpdatesBtn.setOnClickListener(v -> {
v.setEnabled(false); // Prevent double-click
UpdateChecker.forceCheck(this, (version, url, notes) -> {
if (version != null) {
showUpdateBanner(version, url);
} else {
Toast.makeText(this, "No updates available", Toast.LENGTH_SHORT).show();
}
v.setEnabled(true);
});
});
```
--------------------------------
### Manual Update Check Data Flow (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Details the user-initiated manual update check process within the SetupActivity. It covers disabling the button, calling `UpdateChecker.forceCheck`, handling the result (update available or not), and re-enabling the button.
```java
User in SetupActivity (any step)
↓
Clicks update button (🔄 icon)
↓
Button disabled (prevent double-click)
↓
UpdateChecker.forceCheck(context, callback)
→ Clear last check timestamp
→ Execute check immediately
↓
If update available:
→ Show update banner with download button
Else:
→ Show toast: "No updates available"
↓
Re-enable button
```
--------------------------------
### Execute Git Version Control Commands
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Commands to stage the newly created Java file and commit the changes to the local repository with a descriptive message.
```bash
git add app/src/main/java/app/botdrop/ModelListAdapter.java
git commit -m "feat: add ModelListAdapter for RecyclerView"
```
--------------------------------
### Build and Version Control Commands
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Standard shell commands for compiling the Android project using Gradle and committing changes to the repository.
```bash
./gradlew :app:compileDebugJavaWithJavac
git add app/src/main/java/app/botdrop/ChannelFragment.java
git commit -m "feat: save Telegram config to cache in ChannelFragment"
```
--------------------------------
### Configure OpenClaw Network Settings
Source: https://github.com/zhixianio/botdrop-android/blob/master/CLAUDE.md
Configures the OpenClaw network settings to enable autoSelectFamily, which resolves IPv6 connection failures for Telegram bots in Node.js 22+ environments.
```json
{
"channels": {
"telegram": {
"network": { "autoSelectFamily": true }
}
}
}
```
--------------------------------
### Initialize Dashboard UI and Model Loading in Java
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Initializes the UI components for model selection and implements the logic to read and display the current model from the application configuration.
```java
private TextView mCurrentModelText;
private Button mChangeModelButton;
// In onCreate()
mCurrentModelText = findViewById(R.id.current_model_text);
mChangeModelButton = findViewById(R.id.btn_change_model);
mChangeModelButton.setOnClickListener(v -> onChangeModelClick());
loadCurrentModel();
private void loadCurrentModel() {
try {
JSONObject config = BotDropConfig.readConfig();
if (config.has("agents")) {
JSONObject agents = config.getJSONObject("agents");
if (agents.has("defaults")) {
JSONObject defaults = agents.getJSONObject("defaults");
if (defaults.has("model")) {
JSONObject modelObj = defaults.getJSONObject("model");
if (modelObj.has("primary")) {
String model = modelObj.getString("primary");
mCurrentModelText.setText(model);
return;
}
}
}
}
} catch (Exception e) {
Logger.logError(LOG_TAG, "Failed to load current model: " + e.getMessage());
}
mCurrentModelText.setText("—");
}
```
--------------------------------
### Monitor Application Logs
Source: https://github.com/zhixianio/botdrop-android/blob/master/CLAUDE.md
Uses adb logcat to filter and display logs specific to the BotDrop and Termux components. Useful for debugging runtime issues on a connected Android device.
```bash
adb logcat | grep -E "BotDrop|Termux"
```
--------------------------------
### Update Documentation for Crashlytics
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/crashlytics-plan.md
Documents the setup and privacy implications of using Firebase Crashlytics for maintainers. This typically involves creating a new markdown file and linking it from the main README.
```markdown
# Crashlytics Integration
This document outlines how to set up and manage Firebase Crashlytics within the Botdrop Android project.
## Setup for Maintainers
To enable Crashlytics, ensure you have the `google-services.json` file in the `app/` directory. The Gradle build scripts will automatically configure Crashlytics based on this file.
## Privacy Considerations
Crashlytics is configured with safety defaults:
- No Personally Identifiable Information (PII) is collected.
- Crash reporting is disabled by default in debug builds.
- Users can disable crash reporting via the app's debugging preferences.
## Open Source Builds
For open-source builds where `google-services.json` is not available, Crashlytics integration remains inert, ensuring no data is collected or sent.
See [Crashlytics Integration](docs/crashlytics.md) for details on crash reporting.
```
--------------------------------
### Verify and Test SSL Certificate Environment Variables
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/troubleshooting/2026-02-09-slim-bootstrap-ssl-issue.md
Commands to inspect the presence of SSL certificate files and test the Node.js fetch functionality by manually setting the SSL_CERT_FILE environment variable.
```bash
ls -la /data/data/app.botdrop/files/usr/etc/tls/
ls -la /data/data/app.botdrop/files/usr/etc/ssl/certs/
echo $SSL_CERT_FILE
export SSL_CERT_FILE=/data/data/app.botdrop/files/usr/etc/tls/cert.pem
node -e "fetch('https://api.telegram.org').then(r => console.log('SUCCESS:', r.status))"
```
--------------------------------
### Apply Configuration Template in Java
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Applies a configuration template to set API keys and Telegram configurations. Logs errors if any step fails. Handles potential exceptions during the process.
```java
public boolean applyTemplate(ConfigTemplate template) {
try {
// Set provider/model
boolean success = BotDropConfig.setProvider(template.provider, template.model);
if (!success) {
Logger.logError(LOG_TAG, "Failed to set provider/model");
return false;
}
// Set API key
success = BotDropConfig.setApiKey(template.provider, template.apiKey);
if (!success) {
Logger.logError(LOG_TAG, "Failed to set API key");
return false;
}
// Set Telegram config if present
if (template.tgBotToken != null && !template.tgBotToken.isEmpty()) {
success = BotDropConfig.setTelegramChannel(template.tgBotToken, template.tgUserId);
if (!success) {
Logger.logError(LOG_TAG, "Failed to set Telegram config");
return false;
}
}
Logger.logInfo(LOG_TAG, "Config template applied successfully");
return true;
} catch (Exception e) {
Logger.logError(LOG_TAG, "Failed to apply template: " + e.getMessage());
return false;
}
}
}
```
--------------------------------
### Manage Configuration Caching with ConfigTemplate
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Provides a structure for storing user configuration templates and a manager class to persist these settings using SharedPreferences. It includes validation logic and methods to apply templates to the BotDrop configuration system.
```java
public class ConfigTemplate {
public String provider;
public String model;
public String apiKey;
public String tgBotToken;
public String tgUserId;
public boolean isValid() {
return provider != null && !provider.isEmpty()
&& model != null && !model.isEmpty()
&& apiKey != null && !apiKey.isEmpty();
}
}
public class ConfigTemplateCache {
private static final String PREFS_NAME = "botdrop_config_template";
public static void saveTemplate(Context ctx, ConfigTemplate template);
public static ConfigTemplate loadTemplate(Context ctx);
public static void clearTemplate(Context ctx);
public static boolean hasTemplate(Context ctx);
public static boolean applyTemplate(Context ctx, ConfigTemplate template) {
BotDropConfig.setProvider(template.provider, template.model);
BotDropConfig.setApiKey(template.provider, template.apiKey);
if (template.tgBotToken != null) {
BotDropConfig.setTelegramChannel(template.tgBotToken, template.tgUserId);
}
return true;
}
}
```
--------------------------------
### Execute custom shells with RISH
Source: https://github.com/zhixianio/botdrop-android/blob/master/api/rish/README.md
Shows how to specify an alternative shell binary to be executed by the RISH daemon instead of the default /system/bin/sh.
```shell
rish exec /path/to/other/shell
```
--------------------------------
### Configuration Recovery Data Flow (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Describes the data flow for recovering application configuration after reinstallation or corruption. It checks for a saved template, prompts the user to restore, applies the template to `BotDropConfig`, and navigates accordingly.
```java
User reinstalls app or OpenClaw config corrupted
↓
SetupActivity.onCreate()
↓
if (ConfigTemplateCache.hasTemplate(this))
↓
Show dialog: "Restore Configuration?"
↓
User clicks "Use Previous"
↓
ConfigTemplate template = ConfigTemplateCache.loadTemplate(this)
↓
ConfigTemplateCache.applyTemplate(this, template)
→ BotDropConfig.setProvider(...)
→ BotDropConfig.setApiKey(...)
→ BotDropConfig.setTelegramChannel(...) (if exists)
↓
Jump to appropriate step (Channel if no Telegram, Dashboard if complete)
```
--------------------------------
### Set Latest Release in botdrop-packages Repo (Bash)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/troubleshooting/2026-02-09-slim-bootstrap-ssl-issue.md
Instructions for setting the stable bootstrap version as the latest release in the GitHub botdrop-packages repository. This ensures that `/releases/latest/` points to the correct stable version, preventing users from accessing potentially unstable builds.
```bash
# In GitHub botdrop-packages repository:
# https://github.com/zhixianio/botdrop-packages/releases
#
# 1. Find "bootstrap-2026.02.07-r1+botdrop" release
# 2. Click "..." menu → "Set as latest release"
# 3. This ensures /releases/latest/ points to stable version
```
--------------------------------
### Wire Update Button Logic in SetupActivity
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Initializes the update button in the SetupActivity and implements the click listener to invoke the UpdateChecker service, displaying results via Toast.
```java
ImageButton checkUpdatesBtn = findViewById(R.id.setup_check_updates);
checkUpdatesBtn.setOnClickListener(v -> {
v.setEnabled(false);
UpdateChecker.forceCheck(this, (version, url, notes) -> {
v.setEnabled(true);
if (version != null && !version.isEmpty()) {
Toast.makeText(this, "Update available: v" + version, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "No updates available", Toast.LENGTH_SHORT).show();
}
});
});
```
--------------------------------
### Implement Model Selection and Gateway Restart in Java
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Handles the model selection dialog interaction, updates the configuration file, refreshes the cache, and triggers a gateway restart.
```java
private void onChangeModelClick() {
if (!mBound || mBotDropService == null) {
Toast.makeText(this, "Service not available", Toast.LENGTH_SHORT).show();
return;
}
ModelSelectorDialog dialog = new ModelSelectorDialog(this, mBotDropService);
dialog.show((provider, model) -> {
String fullModel = provider + "/" + model;
Logger.logInfo(LOG_TAG, "Changing model to: " + fullModel);
if (updateModelConfig(fullModel)) {
updateModelCache(fullModel);
mCurrentModelText.setText(fullModel);
Toast.makeText(this, "Model changed, restarting gateway...", Toast.LENGTH_SHORT).show();
restartGateway();
} else {
Toast.makeText(this, "Failed to update model", Toast.LENGTH_SHORT).show();
}
});
}
```
--------------------------------
### Implement Model Selection Dialog and Parsing
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Defines the UI structure and logic for a searchable model selection dialog. It includes the ModelInfo data structure, real-time filtering logic, and the callback interface for handling user selections.
```xml
```
```java
public class ModelInfo {
public String fullName;
public String provider;
public String model;
public String input;
public String context;
public boolean isDefault;
}
private void filterModels(String query) {
String lowerQuery = query.toLowerCase();
List filtered = allModels.stream()
.filter(m -> m.fullName.toLowerCase().contains(lowerQuery))
.collect(Collectors.toList());
adapter.updateList(filtered);
}
public interface ModelSelectedCallback {
void onModelSelected(String provider, String model);
}
```
--------------------------------
### Create ConfigTemplate Data Class (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Defines a simple Java data class `ConfigTemplate` to hold configuration fields such as provider, model, and API key. It includes a constructor, a validation method to check for required fields, and an overridden `toString` method for logging purposes. This class is intended for caching user configurations.
```java
package app.botdrop;
/**
* Configuration template data class.
* Holds user configuration for caching and quick recovery.
*/
public class ConfigTemplate {
public String provider; // e.g., "google"
public String model; // e.g., "gemini-3-flash-preview"
public String apiKey; // e.g., "AIzaSy..."
public String tgBotToken; // e.g., "7123456:AAF..." (optional)
public String tgUserId; // e.g., "987654321" (optional)
public ConfigTemplate() {
}
public ConfigTemplate(String provider, String model, String apiKey) {
this.provider = provider;
this.model = model;
this.apiKey = apiKey;
}
/**
* Validate that required fields are present.
* @return true if provider, model, and apiKey are non-empty
*/
public boolean isValid() {
return provider != null && !provider.isEmpty()
&& model != null && !model.isEmpty()
&& apiKey != null && !apiKey.isEmpty();
// tgBotToken and tgUserId are optional
}
@Override
public String toString() {
return "ConfigTemplate{"
+ "provider='" + provider + "'" +
", model='" + model + "'" +
", apiKey='***'" + // Don't log full key
", tgBotToken=" + (tgBotToken != null ? "***" : "null") +
", tgUserId='" + tgUserId + "'" +
'}';
}
}
```
--------------------------------
### Perform Global Action
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/design-android-automation.md
Executes system-level global actions such as back, home, recents, or opening the notification shade.
```APIDOC
## POST /ui/global
### Description
Performs a global system action. These actions affect the overall device navigation or system UI, rather than specific application elements.
### Method
POST
### Endpoint
/ui/global
### Parameters
#### Request Body
- **action** (string) - Required - The global action to perform. Supported actions: `back`, `home`, `recents`, `notifications`.
### Request Example
```json
{
"action": "home"
}
```
### Response
#### Success Response (200)
- **success** (boolean) - Indicates if the global action was initiated successfully.
#### Response Example
```json
{
"success": true
}
```
```
--------------------------------
### Implement ConfigTemplateCache
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/test-reports/2026-02-09-v0.2.2-integration-test.md
A utility class for managing configuration persistence using Android SharedPreferences. It provides methods to save, load, and clear configuration templates, ensuring state consistency across app restarts.
```kotlin
class ConfigTemplateCache(private val prefs: SharedPreferences) {
fun saveTemplate(template: ConfigTemplate) {
prefs.edit().apply {
putString("provider", template.provider)
putString("model", template.model)
putString("apiKey", template.apiKey)
putString("tgBotToken", template.tgBotToken)
putString("tgUserId", template.tgUserId)
apply()
}
}
fun loadTemplate(): ConfigTemplate? {
val provider = prefs.getString("provider", null) ?: return null
return ConfigTemplate(
provider,
prefs.getString("model", null),
prefs.getString("apiKey", null),
prefs.getString("tgBotToken", null),
prefs.getString("tgUserId", null)
)
}
}
```
--------------------------------
### Git Version Control Commands
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Standard git commands used to stage and commit changes to the repository.
```bash
git add app/src/main/java/app/botdrop/ModelSelectorDialog.java
git commit -m "feat: add ModelSelectorDialog with parsing and search logic"
git add app/src/main/java/app/botdrop/UpdateChecker.java
git commit -m "feat: add UpdateChecker.forceCheck for manual update button"
```
--------------------------------
### Run Targeted Unit Tests (Gradle/Bash)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-03-17-openclaw-offline-bundle.md
This command executes specific unit tests for the Android application, focusing on classes related to bundled OpenCLAW utilities and services. It uses Gradle to run tests tagged with specific class names, ensuring that core functionalities related to offline assets and service operations are working correctly. The expected outcome is a 'PASS' status for all targeted tests.
```bash
./gradlew :app:testDebugUnitTest \
--tests app.botdrop.BundledOpenclawUtilsTest \
--tests app.botdrop.BotDropServiceTest \
--tests app.botdrop.OpenclawVersionUtilsTest \
--tests app.botdrop.ChannelSetupHelperTest \
--no-daemon
```
--------------------------------
### Apply Permanent SSL Fix via Symlink
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/troubleshooting/2026-02-09-slim-bootstrap-ssl-issue.md
Creates a symbolic link to the certificate file in the standard location expected by OpenSSL, ensuring compatibility without modifying environment variables.
```bash
mkdir -p /usr/etc/ssl
ln -s /usr/etc/tls/cert.pem /usr/etc/ssl/cert.pem
```
--------------------------------
### Git Version Control Commands
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Standard git commands to stage and commit the changes made to the Android project files.
```bash
git add app/src/main/res/drawable/ic_update.xml app/src/main/res/layout/activity_botdrop_setup.xml
git commit -m "feat: add manual update button to SetupActivity navigation bar"
git add app/src/main/java/app/botdrop/SetupActivity.java
git commit -m "feat: wire manual update check button in SetupActivity"
```
--------------------------------
### Requesting Permissions with Shizuku in Android (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/api/README.md
Demonstrates how to request and handle permissions using the Shizuku library in an Android application. It includes setting up listeners for permission results and checking/requesting the permission itself. This code relies on the Shizuku library.
```java
private void onRequestPermissionsResult(int requestCode, int grantResult) {
boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
// Do stuff based on the result and the request code
}
private final Shizuku.OnRequestPermissionResultListener REQUEST_PERMISSION_RESULT_LISTENER = this::onRequestPermissionsResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER);
// ...
}
@Override
protected void onDestroy() {
// ...
Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER);
// ...
}
private boolean checkPermission(int code) {
if (Shizuku.isPreV11()) {
// Pre-v11 is unsupported
return false;
}
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
// Granted
return true;
} else if (Shizuku.shouldShowRequestPermissionRationale()) {
// Users choose "Deny and don't ask again"
return false;
} else {
// Request the permission
Shizuku.requestPermission(code);
return false;
}
}
```
--------------------------------
### Commit Git Changes (Bash)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
Stages and commits the newly created `ConfigTemplate.java` file to the Git repository. This command is used after implementing the data class to save the changes with a descriptive commit message.
```bash
git add app/src/main/java/app/botdrop/ConfigTemplate.java
git commit -m "feat: add ConfigTemplate data class for config caching"
```
--------------------------------
### Handle Configuration Version Migration in Android
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
This Java code snippet illustrates a version migration process for configuration templates stored in SharedPreferences. It checks the stored configuration version against the current `CONFIG_VERSION`. If the stored version is older, it calls `migrateConfig(ctx, version)` to update the configuration to the latest format, ensuring compatibility with new versions of the application.
```java
private static final int CONFIG_VERSION = 1;
public static ConfigTemplate loadTemplate(Context ctx) {
SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
int version = prefs.getInt("version", 0);
if (version < CONFIG_VERSION) {
migrateConfig(ctx, version);
}
// Load template...
}
```
--------------------------------
### Execute shell commands with RISH
Source: https://github.com/zhixianio/botdrop-android/blob/master/api/rish/README.md
Demonstrates the basic usage of RISH to execute commands on a remote shell. The command passes arguments directly to the backend, effectively replacing standard shell execution.
```shell
rish -c 'ls'
```
--------------------------------
### Commit Documentation Changes (Git)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-03-17-openclaw-offline-bundle.md
This command stages and commits the documentation changes related to the offline OpenCLAW bundle plan. It uses Git to add the specified markdown file to the staging area and then creates a commit with a descriptive message. This ensures that the plan for offline asset bundling is version-controlled.
```bash
git add docs/plans/2026-03-17-openclaw-offline-bundle.md
git commit -m "docs: add offline openclaw bundle plan"
```
--------------------------------
### Android Resource Verification: Gradle Command
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-implementation.md
This command is used to process and verify the Android resources defined in the project. Running `./gradlew :app:processDebugResources` compiles and checks the XML layout files and other resources for errors, ensuring they are correctly integrated into the application build.
```bash
./gradlew :app:processDebugResources
```
--------------------------------
### Implement Force Update Check (Java)
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/plans/2026-02-09-v0.2.2-features-design.md
Enhances the `UpdateChecker` class in Java by adding a `forceCheck` method. This method bypasses the 24-hour throttle by resetting the last check timestamp, allowing immediate update checks.
```java
// UpdateChecker.java
public static void forceCheck(Context ctx, UpdateCallback cb) {
// Clear last check timestamp to force immediate check
SharedPreferences prefs = ctx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putLong(KEY_LAST_CHECK, 0).apply();
// Execute check
check(ctx, cb);
}
```
--------------------------------
### Find UI Elements
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/design-android-automation.md
Finds UI elements within the current window based on specified selectors. Supports finding the first match or all matches, with an optional timeout.
```APIDOC
## POST /ui/find
### Description
Finds UI elements in the current window based on a provided selector. You can specify whether to find the first matching element or all matching elements, and set a timeout for the search.
### Method
POST
### Endpoint
/ui/find
### Parameters
#### Request Body
- **selector** (object) - Required - The criteria to select nodes. Supports various matching attributes like resourceId, className, text, contentDesc, bounds, and state. Can also compose selectors using 'and', 'or', 'not', and 'parent'/'child' constraints.
- **mode** (string) - Optional - Defaults to "first". Can be "first" to return only the first matching node, or "all" to return all matching nodes.
- **timeoutMs** (integer) - Optional - The maximum time in milliseconds to wait for elements to appear. If not specified, a default timeout may be used.
### Request Example
```json
{
"selector": {
"resourceId": "com.example.app:id/myButton",
"text": "Submit"
},
"mode": "first",
"timeoutMs": 5000
}
```
### Response
#### Success Response (200)
- **nodes** (array) - An array of node objects that match the selector. If mode is "first", this array will contain at most one element.
#### Response Example
```json
{
"nodes": [
{
"nodeId": "3",
"package": "com.example.app",
"class": "android.widget.Button",
"text": "Submit",
"contentDesc": null,
"resourceId": "com.example.app:id/myButton",
"bounds": {"x1": 50, "y1": 50, "x2": 150, "y2": 100},
"clickable": true,
"enabled": true,
"visible": true,
"children": []
}
]
}
```
```
--------------------------------
### CMake Build Configuration for 'rish' Library
Source: https://github.com/zhixianio/botdrop-android/blob/master/api/rish/src/main/cpp/CMakeLists.txt
This snippet defines the core build settings for the 'rish' shared library using CMake. It sets the C++ standard, compiler flags for release and debug modes, and links necessary libraries. It also includes a post-build command to strip symbols from the release library.
```cmake
cmake_minimum_required(VERSION 3.31)
project("rish")
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-Werror=format -fdata-sections -ffunction-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Builing Release...")
add_compile_options(-Os -flto -fvisibility=hidden -fvisibility-inlines-hidden)
add_link_options(-flto -Wl,--exclude-libs,ALL -Wl,--gc-sections -Wl,--strip-all)
else ()
message("Builing Debug...")
add_definitions(-DDEBUG)
endif ()
find_package(cxx REQUIRED CONFIG)
add_library(rish SHARED
main.cpp
pts.cpp
rikka_rish_RishTerminal.cpp
rikka_rish_RishHost.cpp)
target_link_libraries(rish log cxx::cxx)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_custom_command(TARGET rish POST_BUILD
COMMAND ${CMAKE_STRIP} --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/librish.so")
endif ()
```
--------------------------------
### Configure SSL Environment Variables in Java Service
Source: https://github.com/zhixianio/botdrop-android/blob/master/docs/troubleshooting/2026-02-09-slim-bootstrap-ssl-issue.md
Modifies the ProcessBuilder environment in a Java service to ensure the SSL_CERT_FILE variable is correctly set for Node.js processes.
```java
pb.environment().put("PREFIX", TermuxConstants.TERMUX_PREFIX_DIR_PATH);
pb.environment().put("HOME", TermuxConstants.TERMUX_HOME_DIR_PATH);
pb.environment().put("PATH", TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH + ":" + System.getenv("PATH"));
pb.environment().put("TMPDIR", TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH);
pb.environment().put("SSL_CERT_FILE", TermuxConstants.TERMUX_PREFIX_DIR_PATH + "/etc/tls/cert.pem");
```