Puppeteer-like API for Android automation - Control Android devices with familiar web automation syntax for testing, scraping, and automation
⚠️ Work In Progress: This repository is currently under active development. Features and APIs may change. Emulator support is in testing phase.
Droideer brings the beloved Puppeteer API to Android automation. Whether you're testing apps, scraping mobile data, or automating workflows, if you've ever automated web browsers, you'll feel right at home automating Android apps!
Perfect for:
- 🧪 End-to-end testing of Android applications
- 📊 Data scraping from mobile apps and services
- 🤖 Workflow automation and repetitive task automation
- 🔍 App behavior analysis and reverse engineering
- 📱 Cross-platform testing across different Android devices
Note: For a visual UI inspector and debugging experience similar to Chrome DevTools, check out our companion repository: Droideer DevTools 🛠️
import { Droideer } from 'droideer';
const device = await Droideer.connect();
const page = await device.launch('com.instagram.android');
await page.type({ resourceId: 'login_username' }, 'john_doe');
await page.type({ resourceId: 'password' }, 'secret123');
await page.click({ text: 'Log In' });
await page.waitForSelector({ text: 'Home' });
await page.screenshot('logged-in.png');
- 🎯 Puppeteer-like API - Familiar syntax for web developers
- 🔍 Smart Element Finding - Multiple selector strategies (text, ID, class, contains)
- 📱 Real Device Control - Direct ADB integration for authentic interactions
- 🤹 Rich Gestures - Tap, swipe, scroll, long press, drag & drop
- ⏰ Auto-waiting - Built-in waits for UI stability and element appearance
- 📸 Screenshot Support - Visual debugging and test verification
- 🔄 App Management - Launch, close, and manage Android applications
- 🧩 Element Properties - Access all element attributes and properties
- 📋 UI Hierarchy Inspection - Comprehensive element tree analysis
- 🌐 Network Monitoring - Track HTTP requests and API calls during automation
- 🎯 Domain-Specific Filtering - Monitor only relevant network traffic
- 📊 Response Capture - Extract JSON responses and API data
- 🔧 Reverse Engineering - Analyze app behavior and API endpoints
- 📱 Mobile App Scraping - Extract data from native Android applications
- 🔄 Infinite Scroll Handling - Automatically handle pagination and lazy loading
- 📊 Structured Data Extraction - Extract listings, properties, products, and more
- 🎯 Element-Specific Targeting - Precise data extraction using resource IDs and selectors
- 💾 Export Support - Save scraped data in JSON, CSV, or custom formats
npm install droideer
- ADB (Android Debug Bridge) installed and in your PATH
- Android device with USB debugging enabled or an emulator
- Node.js 16 or higher
Note: Emulator support is currently in testing phase. Physical devices are recommended for production use.
Some Examples can be checked in the Examples folder.
import { Droideer } from 'droideer';
// Connect to device
const device = await Droideer.connect();
const page = await device.launch('com.android.settings');
// Interact with UI
await page.click({ text: 'Network & internet' });
await page.waitForSelector({ text: 'Wi-Fi' });
await page.screenshot('wifi-settings.png');
await device.disconnect();
import { Droideer } from 'droideer';
const device = await Droideer.connect();
const page = await device.launch('com.example.marketplace');
// Navigate to search
await page.click({ resourceId: 'search_button' });
await page.type({ resourceId: 'search_input' }, 'smartphones');
await page.pressKey(66); // Enter
// Scrape product listings with infinite scroll
const products = [];
let scrollCount = 0;
const maxScrolls = 20;
while (scrollCount < maxScrolls) {
// Extract current page products
const productElements = await page.$$({ resourceId: 'product_item' });
for (const element of productElements) {
const title = await element.getText();
const price = await page.getText({ resourceId: 'product_price' });
products.push({ title, price });
}
// Scroll for more products
await page.scroll('down');
await page.waitForTimeout(2000);
scrollCount++;
// Check if we've reached the end
const noMoreItems = await page.$({ text: 'No more items' });
if (noMoreItems) break;
}
console.log(`Scraped ${products.length} products`);
import { Droideer } from 'droideer';
const device = await Droideer.connect();
// Monitor network activity during app usage
const networkResults = await device.networkMonitor.monitorAction(async () => {
const page = await device.launch('com.booking');
await page.click({ text: 'Search' });
await page.type({ resourceId: 'destination' }, 'Paris');
await page.click({ text: 'Search hotels' });
// Wait for API calls to complete
await page.waitForTimeout(5000);
}, {
targetDomains: ['booking.com', 'api.booking'], // Only monitor Booking APIs
captureResponses: true
});
console.log(`Captured ${networkResults.summary.totalRequests} network requests`);
console.log('API Endpoints:', networkResults.data.apiEndpoints);
The AndroidDevice
class represents a connected Android device.
// Connect to a device
const device = await Droideer.connect();
// Launch an app
const page = await device.launch('com.android.settings');
// Take a screenshot
await device.screenshot('device.png');
// Press hardware buttons
await device.back();
await device.home();
// Disconnect when done
await device.disconnect();
The Page
class represents the current UI view of an Android app.
// Find elements
const element = await page.$({ text: 'Settings' });
const elements = await page.$$({ className: 'android.widget.Button' });
// Convenience methods
const button = await page.findByResourceId('submit_button');
const textElement = await page.findByText('Welcome');
// Interact with UI
await page.click({ text: 'Next' });
await page.type({ resourceId: 'username_field' }, 'john_doe');
await page.scroll('down', 500);
// Wait for elements or conditions
await page.waitForSelector({ text: 'Welcome' });
await page.waitForNavigation();
The AndroidElement
class represents a UI element on the screen.
// Get element properties
const text = element.text;
const resourceId = element.resourceId;
const isEnabled = element.isEnabled;
// Interact with element
await element.click();
await element.type('Hello world');
await element.longPress();
await element.swipeLeft();
Droideer supports multiple selector strategies for finding elements:
// By text
await page.$({ text: 'Login' });
// By partial text
await page.$({ contains: 'Log' });
// By resource ID
await page.$({ resourceId: 'com.example.app:id/username' });
// By class name
await page.$({ className: 'android.widget.EditText' });
// By content description
await page.$({ contentDesc: 'Profile picture' });
// Combined selectors
await page.$({
className: 'android.widget.Button',
text: 'Login',
clickable: true
});
// XPath-like selectors
await page.$x('//android.widget.Button[@text="Submit"]');
// Start monitoring
await device.networkMonitor.startMonitoring({
targetDomains: ['api.example.com'],
captureResponses: true
});
// Perform actions...
// Stop and get results
const results = await device.networkMonitor.stopMonitoring();
import { test, expect } from '@playwright/test';
import { Droideer } from 'droideer';
test('user can complete purchase flow', async () => {
const device = await Droideer.connect();
const page = await device.launch('com.example.shop');
// Test complete user journey
await page.click({ text: 'Shop Now' });
await page.type({ resourceId: 'search' }, 'running shoes');
await page.click({ text: 'Search' });
const firstProduct = await page.$({ resourceId: 'product_item' });
await firstProduct.click();
await page.click({ text: 'Add to Cart' });
await page.click({ resourceId: 'cart_button' });
await page.click({ text: 'Checkout' });
const confirmationText = await page.waitForSelector({ text: 'Order confirmed' });
expect(confirmationText).toBeTruthy();
});
// Scrape real estate listings
const device = await Droideer.connect();
const page = await device.launch('com.idealista.android');
const listings = [];
let hasMorePages = true;
while (hasMorePages) {
const propertyElements = await page.$$({ resourceId: 'property_card' });
for (const property of propertyElements) {
const title = await property.getText();
const price = await page.getText({ resourceId: 'property_price' });
const location = await page.getText({ resourceId: 'property_location' });
listings.push({ title, price, location });
}
// Try to go to next page
const nextButton = await page.$({ text: 'Next' });
if (nextButton) {
await nextButton.click();
await page.waitForTimeout(3000);
} else {
hasMorePages = false;
}
}
console.log(`Scraped ${listings.length} property listings`);
// Monitor network traffic to understand app APIs
const device = await Droideer.connect();
const apiAnalysis = await device.networkMonitor.monitorAction(async () => {
const page = await device.launch('com.airbnb.android');
await page.type({ resourceId: 'search_location' }, 'New York');
await page.click({ text: 'Search' });
await page.waitForTimeout(5000);
// Scroll to trigger pagination APIs
for (let i = 0; i < 5; i++) {
await page.scroll('down');
await page.waitForTimeout(2000);
}
}, {
targetDomains: ['airbnb.com'],
targetKeywords: ['api', 'search', 'listings'],
captureResponses: true
});
console.log('Discovered API endpoints:', apiAnalysis.data.apiEndpoints);
console.log('JSON responses:', apiAnalysis.data.jsonResponses.length);
Droideer is perfect for end-to-end testing of Android applications. See TESTING.md for detailed testing guidelines.
import { Droideer } from 'droideer';
import { test, expect } from 'your-test-framework';
test('should login successfully', async () => {
const device = await Droideer.connect();
const page = await device.launch('com.example.app');
await page.type({ resourceId: 'username' }, 'testuser');
await page.type({ resourceId: 'password' }, 'password123');
await page.click({ text: 'Login' });
const welcomeElement = await page.waitForSelector({ text: 'Welcome' });
expect(welcomeElement).not.toBeNull();
await device.disconnect();
});
This project is currently under active development. Here's what's working and what's planned:
- Core element finding and interaction
- Basic gestures and navigation
- Screenshot capabilities
- App management
- Network monitoring (basic)
- Real device support
- API Documentation - Detailed API reference
- Testing Guide - Guide for using Droideer in testing
- Scraping Guide - Best practices for mobile app scraping
- Network Monitoring - Advanced network monitoring features
- Contributing - Guidelines for contributing to Droideer
Contributions are welcome! Please feel free to submit a Pull Request. See CONTRIBUTING.md for detailed guidelines.
git clone https://github.com/your-username/droideer.git
cd droideer
npm install
npm run build
npm test
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Puppeteer for the web
- Built on top of Android Debug Bridge (ADB)
- Thanks to the Android automation community
Built with ❤️ for Android automation, testing, and data extraction
Star ⭐ this repo if you find it useful!