android-sdd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
const fs_1 = __importDefault(require("fs"));
|
|
31
|
+
const path_1 = __importDefault(require("path"));
|
|
32
|
+
const inquirer = require("inquirer");
|
|
33
|
+
/**
|
|
34
|
+
* GitHub Remote Skills Repo (ZIP)
|
|
35
|
+
*/
|
|
36
|
+
const REMOTE_SKILLS_ZIP = "https://github.com/mozhdehnoury-byte/android-agent-skills/archive/refs/heads/main.zip";
|
|
37
|
+
/**
|
|
38
|
+
* ورود برنامه
|
|
39
|
+
*/
|
|
40
|
+
async function main() {
|
|
41
|
+
const args = process.argv.slice(2);
|
|
42
|
+
const command = args[0];
|
|
43
|
+
if (command === "init") {
|
|
44
|
+
await initRemote();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (command === "install") {
|
|
48
|
+
const selectedSkills = await wizard(skillsPath());
|
|
49
|
+
await install(selectedSkills, skillsPath());
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await wizard(skillsPath());
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* مسیر skills (local fallback)
|
|
56
|
+
*/
|
|
57
|
+
function skillsPath() {
|
|
58
|
+
return path_1.default.join(__dirname, "../skills");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* wizard: انتخاب skill ها
|
|
62
|
+
*/
|
|
63
|
+
async function wizard(basePath) {
|
|
64
|
+
const selectedSkills = {};
|
|
65
|
+
const parentFolders = fs_1.default.readdirSync(basePath);
|
|
66
|
+
for (const parent of parentFolders) {
|
|
67
|
+
const children = fs_1.default.readdirSync(path_1.default.join(basePath, parent));
|
|
68
|
+
const answer = await inquirer.prompt([
|
|
69
|
+
{
|
|
70
|
+
type: "checkbox",
|
|
71
|
+
name: "selected",
|
|
72
|
+
message: `Select ${parent} skills`,
|
|
73
|
+
choices: children
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
selectedSkills[parent] = answer.selected;
|
|
77
|
+
}
|
|
78
|
+
console.log("Selected Skills:");
|
|
79
|
+
console.log(selectedSkills);
|
|
80
|
+
return selectedSkills;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* install: ساخت .agent + کپی skill ها + ساخت config
|
|
84
|
+
*/
|
|
85
|
+
async function install(selectedSkills, basePath) {
|
|
86
|
+
console.log("🚀 Installing Android SDD skills...");
|
|
87
|
+
const agentRoot = path_1.default.join(process.cwd(), ".agent");
|
|
88
|
+
fs_1.default.mkdirSync(agentRoot, { recursive: true });
|
|
89
|
+
for (const [parent, skills] of Object.entries(selectedSkills)) {
|
|
90
|
+
for (const skill of skills) {
|
|
91
|
+
const source = path_1.default.join(basePath, parent, skill, "SKILL.md");
|
|
92
|
+
const targetDir = path_1.default.join(agentRoot, parent, skill);
|
|
93
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
94
|
+
fs_1.default.copyFileSync(source, path_1.default.join(targetDir, "SKILL.md"));
|
|
95
|
+
console.log(`Installed: ${parent}/${skill}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const config = {
|
|
99
|
+
version: "1.0.0",
|
|
100
|
+
installedAt: new Date().toISOString(),
|
|
101
|
+
skills: selectedSkills
|
|
102
|
+
};
|
|
103
|
+
fs_1.default.writeFileSync(path_1.default.join(agentRoot, "config.json"), JSON.stringify(config, null, 2));
|
|
104
|
+
console.log("\n🎉 Done! Skills installed in .agent/");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Remote init (GitHub ZIP → auto-detect folder → wizard)
|
|
108
|
+
*/
|
|
109
|
+
async function initRemote() {
|
|
110
|
+
console.log("🌍 Downloading skills from GitHub...");
|
|
111
|
+
const fetch = (await Promise.resolve().then(() => __importStar(require("node-fetch")))).default;
|
|
112
|
+
const unzipper = await Promise.resolve().then(() => __importStar(require("unzipper")));
|
|
113
|
+
const res = await fetch(REMOTE_SKILLS_ZIP);
|
|
114
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
115
|
+
const zipPath = path_1.default.join(process.cwd(), "skills.zip");
|
|
116
|
+
fs_1.default.writeFileSync(zipPath, buffer);
|
|
117
|
+
console.log("📦 Extracting skills...");
|
|
118
|
+
await fs_1.default.createReadStream(zipPath)
|
|
119
|
+
.pipe(unzipper.Extract({ path: process.cwd() }))
|
|
120
|
+
.promise();
|
|
121
|
+
/**
|
|
122
|
+
* 🔥 AUTO DETECT extracted skills path
|
|
123
|
+
* چون GitHub همیشه یک wrapper folder میسازه
|
|
124
|
+
*/
|
|
125
|
+
const extractedBase = process.cwd();
|
|
126
|
+
const possiblePaths = [
|
|
127
|
+
path_1.default.join(extractedBase, "android-agent-skills-main"),
|
|
128
|
+
];
|
|
129
|
+
let extractedPath = null;
|
|
130
|
+
for (const p of possiblePaths) {
|
|
131
|
+
if (fs_1.default.existsSync(p)) {
|
|
132
|
+
extractedPath = p;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!extractedPath) {
|
|
137
|
+
throw new Error("❌ Could not find skills folder after extraction");
|
|
138
|
+
}
|
|
139
|
+
console.log("🧠 Running wizard...");
|
|
140
|
+
const selectedSkills = await wizard(extractedPath);
|
|
141
|
+
await install(selectedSkills, extractedPath);
|
|
142
|
+
}
|
|
143
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "android-sdd",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Android SDD CLI for managing skills",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"android-sdd": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "ts-node src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/inquirer": "^9.0.10",
|
|
19
|
+
"@types/node": "^25.9.3",
|
|
20
|
+
"typescript": "^5.3.3"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"inquirer": "^8.2.7",
|
|
24
|
+
"node-fetch": "^3.3.2",
|
|
25
|
+
"unzipper": "^0.12.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: baseline-profile-generator
|
|
3
|
+
description: >
|
|
4
|
+
Generating Baseline Profiles for Android app startup and runtime optimization.
|
|
5
|
+
Load this skill when setting up Baseline Profile generation, writing profile
|
|
6
|
+
rules, integrating with CI, or measuring startup improvement.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Baseline Profile Generator
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Baseline Profiles pre-compile critical code paths (startup, common user journeys) so the Android Runtime (ART) doesn't need to JIT-compile them at runtime. This reduces app startup time and improves runtime performance — typically 20-40% faster startup on first launch after install.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- Baseline Profiles target **critical user journeys** — not the entire app
|
|
19
|
+
- Profiles are included in the **release APK/AAB** — they ship to users
|
|
20
|
+
- Profile generation requires a **physical device or emulator** running the app
|
|
21
|
+
- Regenerate profiles when **critical code paths change** significantly
|
|
22
|
+
- Measure startup **before and after** to verify improvement
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
[versions]
|
|
30
|
+
androidx-benchmark = "1.3.1"
|
|
31
|
+
androidx-profileinstaller = "1.3.1"
|
|
32
|
+
|
|
33
|
+
[libraries]
|
|
34
|
+
androidx-benchmark-macro = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "androidx-benchmark" }
|
|
35
|
+
androidx-profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version.ref = "androidx-profileinstaller" }
|
|
36
|
+
androidx-test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
|
|
37
|
+
|
|
38
|
+
[plugins]
|
|
39
|
+
androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidx-benchmark" }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```kotlin
|
|
43
|
+
// settings.gradle.kts
|
|
44
|
+
include(":baselineprofile")
|
|
45
|
+
|
|
46
|
+
// baselineprofile/build.gradle.kts
|
|
47
|
+
plugins {
|
|
48
|
+
alias(libs.plugins.android.test)
|
|
49
|
+
alias(libs.plugins.androidx.baselineprofile)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
android {
|
|
53
|
+
namespace = "com.example.baselineprofile"
|
|
54
|
+
targetProjectPath = ":app"
|
|
55
|
+
|
|
56
|
+
defaultConfig {
|
|
57
|
+
minSdk = 28 // Baseline Profiles require API 28+
|
|
58
|
+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
dependencies {
|
|
63
|
+
implementation(libs.androidx.test.uiautomator)
|
|
64
|
+
implementation(libs.androidx.benchmark.macro)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// app/build.gradle.kts
|
|
68
|
+
plugins {
|
|
69
|
+
alias(libs.plugins.androidx.baselineprofile)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
dependencies {
|
|
73
|
+
implementation(libs.androidx.profileinstaller)
|
|
74
|
+
baselineProfile(project(":baselineprofile"))
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Writing Profile Rules
|
|
81
|
+
|
|
82
|
+
```kotlin
|
|
83
|
+
// baselineprofile/src/main/kotlin/com/example/baselineprofile/BaselineProfileGenerator.kt
|
|
84
|
+
|
|
85
|
+
@RunWith(AndroidJUnit4::class)
|
|
86
|
+
class BaselineProfileGenerator {
|
|
87
|
+
|
|
88
|
+
@get:Rule
|
|
89
|
+
val rule = BaselineProfileRule()
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun generate() = rule.collect(
|
|
93
|
+
packageName = "com.example.app",
|
|
94
|
+
profileBlock = {
|
|
95
|
+
// ✅ Journey 1: App startup
|
|
96
|
+
startActivityAndWait()
|
|
97
|
+
|
|
98
|
+
// ✅ Journey 2: Navigate to main screen
|
|
99
|
+
device.findObject(By.text("Home")).click()
|
|
100
|
+
device.waitForIdle()
|
|
101
|
+
|
|
102
|
+
// ✅ Journey 3: Open a list and scroll
|
|
103
|
+
device.findObject(By.text("Users")).click()
|
|
104
|
+
device.waitForIdle()
|
|
105
|
+
|
|
106
|
+
device.findObject(By.res("user_list")).also { list ->
|
|
107
|
+
list.setGestureMargin(device.displayWidth / 5)
|
|
108
|
+
list.fling(Direction.DOWN)
|
|
109
|
+
device.waitForIdle()
|
|
110
|
+
list.fling(Direction.UP)
|
|
111
|
+
device.waitForIdle()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ✅ Journey 4: Open detail screen
|
|
115
|
+
device.findObject(By.text("Ali Rezaei")).click()
|
|
116
|
+
device.waitForIdle()
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Generating the Profile
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# ✅ Generate profile — runs on connected device/emulator
|
|
128
|
+
./gradlew :baselineprofile:generateBaselineProfile
|
|
129
|
+
|
|
130
|
+
# ✅ Generated file location
|
|
131
|
+
# app/src/main/baseline-prof.txt
|
|
132
|
+
|
|
133
|
+
# ✅ Verify file was generated
|
|
134
|
+
cat app/src/main/baseline-prof.txt
|
|
135
|
+
# Should contain lines like:
|
|
136
|
+
# HSPLcom/example/app/MainActivity;->onCreate(...)V
|
|
137
|
+
# Lcom/example/app/ui/UserListScreen;
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Generated Profile Format
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
# baseline-prof.txt — auto-generated, commit to version control
|
|
146
|
+
HSPLcom/example/app/MainActivity;->onCreate(Landroid/os/Bundle;)V
|
|
147
|
+
HSPLcom/example/app/MainActivity;->onResume()V
|
|
148
|
+
Lcom/example/app/ui/UserListScreen;
|
|
149
|
+
Lcom/example/app/data/UserRepository;
|
|
150
|
+
PLcom/example/app/ui/UserCard;->invoke(Landroidx/compose/runtime/Composer;I)V
|
|
151
|
+
|
|
152
|
+
# H = Hot (compiled with full optimizations)
|
|
153
|
+
# S = Startup (compiled for startup path)
|
|
154
|
+
# P = Post-startup (compiled after startup)
|
|
155
|
+
# L = class loaded at startup
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Measuring Startup Improvement
|
|
161
|
+
|
|
162
|
+
```kotlin
|
|
163
|
+
// ✅ Macrobenchmark test to measure startup time
|
|
164
|
+
@RunWith(AndroidJUnit4::class)
|
|
165
|
+
class StartupBenchmark {
|
|
166
|
+
|
|
167
|
+
@get:Rule
|
|
168
|
+
val benchmarkRule = MacrobenchmarkRule()
|
|
169
|
+
|
|
170
|
+
@Test
|
|
171
|
+
fun startupCompilationNone() = benchmarkRule.measureRepeated(
|
|
172
|
+
packageName = "com.example.app",
|
|
173
|
+
metrics = listOf(StartupTimingMetric()),
|
|
174
|
+
compilationMode = CompilationMode.None(), // no pre-compilation
|
|
175
|
+
startupMode = StartupMode.COLD,
|
|
176
|
+
iterations = 5,
|
|
177
|
+
measureBlock = { startActivityAndWait() }
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@Test
|
|
181
|
+
fun startupWithBaselineProfile() = benchmarkRule.measureRepeated(
|
|
182
|
+
packageName = "com.example.app",
|
|
183
|
+
metrics = listOf(StartupTimingMetric()),
|
|
184
|
+
compilationMode = CompilationMode.Partial(
|
|
185
|
+
baselineProfileMode = BaselineProfileMode.Require
|
|
186
|
+
),
|
|
187
|
+
startupMode = StartupMode.COLD,
|
|
188
|
+
iterations = 5,
|
|
189
|
+
measureBlock = { startActivityAndWait() }
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## ProfileInstaller
|
|
197
|
+
|
|
198
|
+
```kotlin
|
|
199
|
+
// ✅ ProfileInstaller installs the profile at app install time
|
|
200
|
+
// No code needed — adding the dependency is sufficient
|
|
201
|
+
dependencies {
|
|
202
|
+
implementation(libs.androidx.profileinstaller)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ✅ Force install profile for testing (debug builds)
|
|
206
|
+
class MainActivity : ComponentActivity() {
|
|
207
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
208
|
+
super.onCreate(savedInstanceState)
|
|
209
|
+
if (BuildConfig.DEBUG) {
|
|
210
|
+
ProfileVerifier.getCompilationStatusAsync()
|
|
211
|
+
.addListener({
|
|
212
|
+
val status = ProfileVerifier.getCompilationStatusAsync().get()
|
|
213
|
+
Log.d("Profile", "Status: ${status.profileInstallResultCode}")
|
|
214
|
+
}, ContextCompat.getMainExecutor(this))
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## CI Integration
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
# .github/workflows/generate-baseline-profile.yml
|
|
226
|
+
name: Generate Baseline Profile
|
|
227
|
+
|
|
228
|
+
on:
|
|
229
|
+
workflow_dispatch: # manual trigger
|
|
230
|
+
schedule:
|
|
231
|
+
- cron: '0 0 * * 1' # weekly on Monday
|
|
232
|
+
|
|
233
|
+
jobs:
|
|
234
|
+
generate-profile:
|
|
235
|
+
runs-on: macos-latest # macOS for Android emulator acceleration
|
|
236
|
+
steps:
|
|
237
|
+
- uses: actions/checkout@v4
|
|
238
|
+
|
|
239
|
+
- name: Enable KVM
|
|
240
|
+
run: |
|
|
241
|
+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
242
|
+
sudo udevadm control --reload-rules
|
|
243
|
+
sudo udevadm trigger --name-match=kvm
|
|
244
|
+
|
|
245
|
+
- uses: reactivecircus/android-emulator-runner@v2
|
|
246
|
+
with:
|
|
247
|
+
api-level: 34
|
|
248
|
+
target: google_apis
|
|
249
|
+
arch: x86_64
|
|
250
|
+
script: ./gradlew :baselineprofile:generateBaselineProfile
|
|
251
|
+
|
|
252
|
+
- name: Commit generated profile
|
|
253
|
+
run: |
|
|
254
|
+
git config user.email "ci@example.com"
|
|
255
|
+
git config user.name "CI Bot"
|
|
256
|
+
git add app/src/main/baseline-prof.txt
|
|
257
|
+
git commit -m "Update Baseline Profile" || echo "No changes to commit"
|
|
258
|
+
git push
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Anti-Patterns
|
|
264
|
+
|
|
265
|
+
- Not committing `baseline-prof.txt` to version control — profile lost between builds
|
|
266
|
+
- Covering only app startup — also profile common user journeys (lists, navigation)
|
|
267
|
+
- Never regenerating the profile — drifts from actual hot paths over time
|
|
268
|
+
- Using emulator without Google Play — profile generation requires Play services
|
|
269
|
+
- Not measuring startup improvement — can't verify benefit
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Related Skills
|
|
274
|
+
- `baseline-profile` — applying and using Baseline Profiles
|
|
275
|
+
- `benchmark` — measuring performance with Macrobenchmark
|
|
276
|
+
- `startup-optimization` — app startup performance
|
|
277
|
+
- `compose-performance` — Compose-specific optimization
|