googlesamples/easypermissions
Simplify Android M system permissions
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓15 active contributors
- ✓Apache-2.0 licensed
- ✓CI configured
Show all 6 evidence items →Show less
- ✓Tests present
- ⚠Stale — last commit 3y ago
- ⚠Concentrated ownership — top contributor handles 56% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/googlesamples/easypermissions)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/googlesamples/easypermissions on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: googlesamples/easypermissions
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/googlesamples/easypermissions shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 15 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 3y ago
- ⚠ Concentrated ownership — top contributor handles 56% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live googlesamples/easypermissions
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/googlesamples/easypermissions.
What it runs against: a local clone of googlesamples/easypermissions — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in googlesamples/easypermissions | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 1041 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of googlesamples/easypermissions. If you don't
# have one yet, run these first:
#
# git clone https://github.com/googlesamples/easypermissions.git
# cd easypermissions
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of googlesamples/easypermissions and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "googlesamples/easypermissions(\\.git)?\\b" \\
&& ok "origin remote is googlesamples/easypermissions" \\
|| miss "origin remote is not googlesamples/easypermissions (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java" \\
&& ok "easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java" \\
|| miss "missing critical file: easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java"
test -f "easypermissions/src/main/java/pub/devrel/easypermissions/helper/PermissionHelper.java" \\
&& ok "easypermissions/src/main/java/pub/devrel/easypermissions/helper/PermissionHelper.java" \\
|| miss "missing critical file: easypermissions/src/main/java/pub/devrel/easypermissions/helper/PermissionHelper.java"
test -f "easypermissions/src/main/java/pub/devrel/easypermissions/PermissionRequest.java" \\
&& ok "easypermissions/src/main/java/pub/devrel/easypermissions/PermissionRequest.java" \\
|| miss "missing critical file: easypermissions/src/main/java/pub/devrel/easypermissions/PermissionRequest.java"
test -f "easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogFragmentCompat.java" \\
&& ok "easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogFragmentCompat.java" \\
|| miss "missing critical file: easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogFragmentCompat.java"
test -f "easypermissions/src/main/java/pub/devrel/easypermissions/helper/ActivityPermissionHelper.java" \\
&& ok "easypermissions/src/main/java/pub/devrel/easypermissions/helper/ActivityPermissionHelper.java" \\
|| miss "missing critical file: easypermissions/src/main/java/pub/devrel/easypermissions/helper/ActivityPermissionHelper.java"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1041 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1011d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/googlesamples/easypermissions"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
EasyPermissions is a lightweight Android wrapper library that simplifies runtime permission handling for Android M (API 23+) and higher. It eliminates boilerplate code for requesting, checking, and handling system permission grants by providing clean Java APIs and an @AfterPermissionGranted annotation to automatically invoke methods once permissions are granted. Two-module structure: easypermissions/ is the core library module (located at easypermissions/src/main/java/pub/devrel/easypermissions/) containing EasyPermissions.java, AfterPermissionGranted.java, AppSettingsDialog.java, and support activity. app/ is a sample Android application demonstrating usage with MainActivity.java and MainFragment.java.
👥Who it's for
Android developers (particularly those using AndroidX in Activities and Fragments) who need to implement runtime permissions for features like camera, location, or file access without writing repetitive permission-checking and callback-handling code.
🌱Maturity & risk
Production-ready and actively maintained. The library is published to Maven Central, has reached v3.0.0 with AndroidX support, includes CI/CD via GitHub Actions (.github/workflows/test.yml), and is owned by Google Samples. The codebase is stable and widely used in production Android apps.
Low risk. The library has a minimal dependency footprint (only AndroidX core, appcompat, and annotation libraries), a clear, audited codebase of ~126KB, and regular maintenance. No single-maintainer risk due to Google backing. Main consideration: it only wraps the native Android permissions API, so it's bound to Android framework limitations.
Active areas of work
Active maintenance with CI tests running on push (via .github/workflows/test.yml). The project maintains compatibility with current Android API levels (compileSdkVersion 30 visible in build config). No specific open PRs or major features are evident, suggesting stability-focused maintenance.
🚀Get running
git clone https://github.com/googlesamples/easypermissions.git
cd easypermissions
./gradlew build
./gradlew installDebug # or run via Android Studio
Daily commands:
Open the project in Android Studio, sync Gradle, then run the app module on an emulator or device. Alternatively: ./gradlew :app:assembleDebug && ./gradlew :app:installDebug.
🗺️Map of the codebase
easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java— Core public API entry point that implements the permission request logic, rationale dialogs, and result handling—all contributors must understand the main permission flow here.easypermissions/src/main/java/pub/devrel/easypermissions/helper/PermissionHelper.java— Abstract base interface defining the permission helper contract that all platform-specific helpers implement—critical for understanding the strategy pattern used throughout.easypermissions/src/main/java/pub/devrel/easypermissions/PermissionRequest.java— Data model that encapsulates a permission request with permissions, request code, and rationale—essential for understanding how requests are constructed and passed through the library.easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogFragmentCompat.java— Modern dialog fragment implementation for displaying rationale explanations before requesting permissions—critical for the UX flow and androidx compatibility.easypermissions/src/main/java/pub/devrel/easypermissions/helper/ActivityPermissionHelper.java— Activity-level permission helper that handles direct Activity-based permission requests—foundational for understanding how helpers differ by component type.easypermissions/src/main/java/pub/devrel/easypermissions/AppSettingsDialog.java— Dialog that navigates users to app settings when permissions are permanently denied—essential for the recovery/fallback flow.easypermissions/easypermissions/build.gradle— Library module build configuration with AndroidX and support library versions—critical for dependency management and version compatibility.
🛠️How to make changes
Add support for a new Android component type (e.g., Fragment subclass)
- Create a new helper class extending PermissionHelper or BaseSupportPermissionsHelper (
easypermissions/src/main/java/pub/devrel/easypermissions/helper/NewComponentPermissionHelper.java) - Implement requestPermissions() to call your component's requestPermissions() method (
easypermissions/src/main/java/pub/devrel/easypermissions/helper/NewComponentPermissionHelper.java) - Add factory logic to PermissionHelper.newInstance() to detect and instantiate your helper (
easypermissions/src/main/java/pub/devrel/easypermissions/helper/PermissionHelper.java) - Add unit tests in the testhelper package to verify onRequestPermissionsResult() is correctly dispatched (
easypermissions/src/test/java/pub/devrel/easypermissions/testhelper/TestNewComponent.java)
Customize rationale dialog appearance (text, buttons, theme)
- Create a RationaleDialogConfig object with custom message, positive/negative button text, and theme resource (
easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogConfig.java) - Pass the config to EasyPermissions.requestPermissions() via PermissionRequest.setRationaleDialogConfig() (
easypermissions/src/main/java/pub/devrel/easypermissions/PermissionRequest.java) - The RationaleDialogFragmentCompat or RationaleDialogFragment will apply the config when rendering (
easypermissions/src/main/java/pub/devrel/easypermissions/RationaleDialogFragmentCompat.java)
Handle permanently denied permissions (app settings flow)
- After permission denial, check if permission is permanently denied using EasyPermissions.somePermissionPermanentlyDenied() (
easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java) - Create and show AppSettingsDialog with a custom rationale message (
easypermissions/src/main/java/pub/devrel/easypermissions/AppSettingsDialog.java) - The dialog will have an 'Open Settings' button that launches Settings via intent with FLAG_ACTIVITY_NEW_TASK (
easypermissions/src/main/java/pub/devrel/easypermissions/AppSettingsDialog.java)
Add a callback that runs after specific permissions are granted
- Annotate a method in your Activity or Fragment with @AfterPermissionGranted and assign a requestCode (
easypermissions/src/main/java/pub/devrel/easypermissions/AfterPermissionGranted.java) - Call EasyPermissions.onRequestPermissionsResult() in your activity/fragment's onRequestPermissionsResult() (
easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java) - EasyPermissions uses reflection to find and invoke your annotated method if all permissions for that requestCode were granted (
easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java)
🪤Traps & gotchas
- Annotation processing via reflection requires methods annotated with
@AfterPermissionGrantedto be void and parameterless; attempting to use instance state requires manual save/restore viaonSaveInstanceState(). - The library only handles runtime permissions (Android M+); apps targeting minSdkVersion < 23 must still handle manifest-only permissions separately.
AppSettingsDialog.javausesAppSettingsDialogHolderActivityas a holder; this hidden activity must be declared in your manifest (check that the library's manifest is properly merged).- Denying a permission with 'don't ask again' requires manual guidance to Settings; there is no programmatic override.
🏗️Architecture
💡Concepts to learn
- Android M Runtime Permissions Model — EasyPermissions wraps the Android M (API 23) permission system; understanding the distinction between manifest permissions and runtime permission requests is core to using this library
- Annotation Processing & Reflection — The
@AfterPermissionGrantedannotation uses reflection at runtime to discover and invoke matching methods; understanding this pattern helps debug and extend the library - Activity Result Contract Pattern — EasyPermissions abstracts the legacy
onRequestPermissionsResult()callback model; modern Android uses ActivityResultContracts, and understanding the evolution is useful for maintenance - Permission Denial Rationale UI — The library includes
AppSettingsDialogto guide users to app settings after denial with 'don't ask again'; understanding Android's permission lifecycle (denied vs. permanently denied) is essential - Fragment Lifecycle & Permission Callbacks — EasyPermissions supports both Activities and Fragments; fragments have distinct lifecycle and permission callback handling that must be properly coordinated
🔗Related repos
VMadalin/easypermissions-ktx— Official Kotlin extension library for EasyPermissions, adding coroutine-based and extension function APIs for Kotlin developerspermissions-dispatcher/PermissionsDispatcher— Alternative Android permissions library using annotation processing and code generation; comparable feature set but different API designgoogle/accompanist-permissions— Jetpack Compose-first permissions wrapper; the modern declarative alternative for apps using Compose UIgooglesamples/android-architecture— Google's broader Android architecture samples repo; provides context for best practices in Activity/Fragment patterns that EasyPermissions builds upon
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for PermissionHelper interface implementations
The easypermissions/src/test directory exists but appears empty. The helper classes (ActivityPermissionHelper, AppCompatActivityPermissionsHelper, SupportFragmentPermissionHelper, LowApiPermissionsHelper) lack test coverage. These are critical permission-handling components that should have comprehensive unit tests covering edge cases like denied permissions, permission groups, and API level variations.
- [ ] Create test classes in easypermissions/src/test/java/pub/devrel/easypermissions/helper/ for each PermissionHelper implementation
- [ ] Add tests for shouldShowRequestPermissionRationale() behavior across API levels
- [ ] Add tests for requestPermissions() with various permission combinations
- [ ] Add tests for hasPermissions() validation logic
- [ ] Run tests with testOptions.unitTests.includeAndroidResources enabled to validate AndroidManifest behavior
Add GitHub Actions workflow for Android instrumentation tests
The .github/workflows/test.yml exists but there's no visible instrumentation test workflow. The sample app (app/src/main/java/pub/devrel/easypermissions/sample/) contains MainActivity and MainFragment which should have integration tests verifying the permission request flow end-to-end on actual Android versions.
- [ ] Create .github/workflows/instrumentation-tests.yml that runs on API 30 (matching compileSdkVersion)
- [ ] Configure Firebase Test Lab or Android Emulator with appropriate API levels (14, 23, 30+) to verify backward compatibility
- [ ] Add test steps for sample app permission flows: requesting CAMERA, LOCATION, and multi-permission scenarios
- [ ] Ensure tests validate RationaleDialogFragment and AppSettingsDialog behaviors
Add integration tests for RationaleDialogFragment and AppSettingsDialog
RationaleDialogFragment, RationaleDialogFragmentCompat, and AppSettingsDialog are UI components critical to the permission flow but lack visible test coverage. These dialog components need tests to verify proper initialization, configuration handling, and click listener behavior with different RationaleDialogConfig scenarios.
- [ ] Create easypermissions/src/test/java/pub/devrel/easypermissions/RationaleDialogFragmentTest.java
- [ ] Add tests for RationaleDialogConfig properties (title, message, positiveButton, negativeButton, cancelable)
- [ ] Create AppSettingsDialogTest.java to verify dialog launch behavior and permission checking
- [ ] Test both FragmentCompat and regular Fragment versions with different AndroidX/Support Library scenarios
- [ ] Add FragmentScenario or FragmentFactory tests for proper fragment lifecycle handling
🌿Good first issues
- Add unit tests for
EasyPermissions.hasPermissions()with edge cases (empty array, duplicate permissions, null checks) in a new test file undereasypermissions/src/test/ - Document the annotation processing pipeline with inline code comments in
EasyPermissions.javaexplaining how@AfterPermissionGrantedmethods are discovered and invoked via reflection - Create a Kotlin extension library or sample demonstrating idiomatic Kotlin usage (e.g., suspend functions or Flow-based callbacks) as a companion to the existing Java API
⭐Top contributors
Click to expand
Top contributors
- @samtstern — 56 commits
- @SUPERCILEX — 15 commits
- [@Henrique Faria](https://github.com/Henrique Faria) — 7 commits
- @henriquenfaria — 6 commits
- @seventhmoon — 3 commits
📝Recent commits
Click to expand
Recent commits
2b2b5c4— Move to GitHub Actions (#326) (samtstern)1d8c6e0— Add the @IntRange annotation to requestCode to prevent invalid input (#325) (tall-nuts)8952eac— Move from JCenter to Maven Central (#323) (samtstern)5da4f24— Add link to easypermissions-ktx (samtstern)bf3cd48— Changed getContext() to requireContext(), to ensure it is not null. (#309) (seventhmoon)4f4d305— Updating module 'app' to Java 8 syntax (#308) (seventhmoon)eba11f4— Updating project to support Android Studio 4.0 (#307) (seventhmoon)43e63a5— Put in defense for issue 278 (#290) (samtstern)2d46e44— Fix typo in README (#287) (mikebayles)67d611b— Migrate to AndroidX (#272) (samtstern)
🔒Security observations
The EasyPermissions library codebase has a moderate security posture with several concerns primarily related to outdated dependencies and configuration. The most critical issues are: (1) significantly outdated AndroidX dependencies with known vulnerabilities, (2) deprecated Android SDK versions not meeting Google Play requirements, and (3) excessive legacy API support (minSdkVersion 14). The codebase itself appears to be a permissions wrapper library without obvious code-level vulnerabilities like injection risks or hardcoded secrets. However, the dependency and build configuration issues could expose applications using this library to known vulnerabilities. Immediate action should focus on updating AndroidX dependencies and modernizing the target/compile SDK versions.
- High · Outdated AndroidX Dependencies —
app/build.gradle (lines with androidx dependencies). The build.gradle file specifies androidx.appcompat:appcompat:1.1.0 and androidx.annotation:annotation:1.1.0, which are significantly outdated. These versions were released in 2019 and contain known security vulnerabilities that have been patched in newer releases. Modern versions provide critical security fixes for UI rendering, data handling, and framework vulnerabilities. Fix: Update to the latest stable versions of androidx dependencies. Use androidx.appcompat:appcompat:1.6.x or higher and androidx.annotation:annotation:1.7.x or higher. Run 'gradle dependencyUpdates' to identify all outdated dependencies. - Medium · Proguard Configuration Exposure Risk —
app/proguard-rules.pro and easypermissions/proguard-rules.pro. The ProGuard configuration files (app/proguard-rules.pro and easypermissions/proguard-rules.pro) exist in the repository. If these files contain keep rules that are too permissive, they could expose sensitive class names, method signatures, or prevent important security-related obfuscation. Fix: Review ProGuard keep rules to ensure they only preserve what is necessary for functionality. Avoid using wildcards like '-keep class * { *; }'. Ensure sensitive permission-related classes are properly obfuscated while maintaining required API surfaces. - Medium · Deprecated Compile SDK Version —
app/build.gradle (compileSdkVersion 30). The project uses compileSdkVersion 30, which is outdated. As of 2024, Google Play requires apps to target API 34 or higher. Using outdated SDKs misses critical security patches, privacy improvements, and security-related API changes in newer Android versions. Fix: Update compileSdkVersion to 34 or higher, and update targetSdkVersion accordingly. Ensure compatibility testing is performed after upgrading. - Medium · Low Target SDK Version —
app/build.gradle (targetSdkVersion 30). The targetSdkVersion is set to 30, which is outdated. Google Play requires targetSdkVersion 34 or higher for new apps, and existing apps must comply with this requirement. Lower target SDK versions miss important permission model changes, runtime permission enhancements, and security hardening introduced in newer Android versions. Fix: Update targetSdkVersion to 34 or higher. Test the application thoroughly against the new Android version's behaviors and permission models. - Low · Missing Gradle Wrapper Verification —
gradle/wrapper/ directory and gradle/wrapper/gradle-wrapper.properties. The Gradle wrapper is present but there is no verification mechanism (gradle-wrapper.properties SHA-256 checksum or wrapper-validation-action) visible. This could allow potential Man-in-the-Middle attacks or tampering with the gradle wrapper jar. Fix: Implement Gradle wrapper checksum validation using GitHub Actions 'gradle/wrapper-validation-action' or add SHA-256 verification in the build process. Document expected wrapper checksums in the project. - Low · MinSdkVersion 14 is Extremely Legacy —
app/build.gradle (minSdkVersion 14). The minSdkVersion is set to 14 (Android 4.0, released 2011). Supporting such ancient API levels introduces significant security risks as those devices never receive security updates and lack basic security features available in modern Android. Fix: Increase minSdkVersion to at least 21 (Android 5.0) or higher. This aligns with industry standards and reduces the attack surface. Most devices in use today are on API 24 or higher.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.