{"id":22165805,"url":"https://github.com/tutorialsandroid/filepicker","last_synced_at":"2026-05-25T09:09:41.757Z","repository":{"id":40362890,"uuid":"185988544","full_name":"TutorialsAndroid/FilePicker","owner":"TutorialsAndroid","description":"Android Library to select files/directories from Device Storage.","archived":false,"fork":false,"pushed_at":"2025-03-26T18:01:06.000Z","size":414,"stargazers_count":138,"open_issues_count":6,"forks_count":25,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-16T19:06:21.407Z","etag":null,"topics":["android-application","android-development","android-library","filepicker","filepickerdialog"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TutorialsAndroid.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-10T12:56:31.000Z","updated_at":"2025-04-24T11:15:29.000Z","dependencies_parsed_at":"2025-03-26T18:37:37.546Z","dependency_job_id":null,"html_url":"https://github.com/TutorialsAndroid/FilePicker","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TutorialsAndroid%2FFilePicker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TutorialsAndroid%2FFilePicker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TutorialsAndroid%2FFilePicker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TutorialsAndroid%2FFilePicker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TutorialsAndroid","download_url":"https://codeload.github.com/TutorialsAndroid/FilePicker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254592395,"owners_count":22097013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-application","android-development","android-library","filepicker","filepickerdialog"],"created_at":"2024-12-02T05:16:42.820Z","updated_at":"2026-05-25T09:09:41.749Z","avatar_url":"https://github.com/TutorialsAndroid.png","language":"Java","funding_links":["https://github.com/sponsors/TutorialsAndroid"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg width=\"96\" src=\"https://github.com/TutorialsAndroid/FilePicker/blob/master/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png\" alt=\"FilePicker logo\"\u003e\n\n# FilePicker\n\n**A lightweight Android file and directory picker library for Java/Kotlin Android apps.**\n\n[![API](https://img.shields.io/badge/API-23%2B-brightgreen.svg?style=flat)](#requirements)\n![Maven Central](https://img.shields.io/maven-central/v/io.github.tutorialsandroid/filepicker)\n[![](https://jitpack.io/v/TutorialsAndroid/FilePicker.svg)](https://jitpack.io/#TutorialsAndroid/FilePicker)\n[![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg)](https://opensource.org/licenses/Apache-2.0)\n[![AndroidX](https://img.shields.io/badge/AndroidX-supported-brightgreen)](#requirements)\n\nSelect files, folders, or both from device storage with single-selection and multi-selection support.\n\n\u003c/div\u003e\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n## 🌐 Explore the Official FilePicker Landing Page\n\nExperience the complete documentation, installation guide, Android storage notes, Play Store compliance guidance, customization examples, and API reference in a clean visual format.\n\n\u003cbr\u003e\n\n\u003ca href=\"https://tutorialsandroid.github.io/FilePicker/\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/View%20Landing%20Page-FilePicker%20Docs-2563EB?style=for-the-badge\u0026logo=githubpages\u0026logoColor=white\" alt=\"View FilePicker Landing Page\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/TutorialsAndroid/FilePicker\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/GitHub-Repository-181717?style=for-the-badge\u0026logo=github\u0026logoColor=white\" alt=\"GitHub Repository\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/sponsors/TutorialsAndroid\" target=\"_blank\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Sponsor%20Project-GitHub%20Sponsors-EA4AAA?style=for-the-badge\u0026logo=githubsponsors\u0026logoColor=white\" alt=\"Sponsor FilePicker\"\u003e\n\u003c/a\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003e📦 Installation\u003c/strong\u003e\u003cbr\u003eMaven Central + JitPack setup\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003e🛡️ Play Store Guide\u003c/strong\u003e\u003cbr\u003eStorage policy and compliance notes\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003e🎨 Customization\u003c/strong\u003e\u003cbr\u003eTheme, dialog size, filters, and examples\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr\u003e\n\n\u003cstrong\u003eRecommended for new users:\u003c/strong\u003e Start with the landing page for the fastest setup and complete Android version-wise guidance.\n\n\u003c/div\u003e\n\n---\n\nCommunity links:\n\n- Instagram: https://instagram.com/coderx09\n- Telegram: https://t.me/a_masram444\n\n---\n\n## Contributors\n\nThanks to all contributors who helped improve this library.\n\n- [M\u0026R Games](https://github.com/mrgames13)\n- [Hatzen](https://github.com/Hatzen)\n\n---\n\n## ❤️ Support the Project\n\nIf this library helps you, please consider supporting my open-source work.\n\n\u003cp align=\"start\"\u003e\n  \u003ca href=\"https://github.com/sponsors/TutorialsAndroid\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Sponsor%20on-GitHub-EA4AAA?style=for-the-badge\u0026logo=githubsponsors\u0026logoColor=white\" alt=\"Sponsor on GitHub\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Table of Contents\n\n- [Overview](#overview)\n- [What is new in v10](#what-is-new-in-v10)\n- [Important Android storage reality](#important-android-storage-reality)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Permissions by Android version](#permissions-by-android-version)\n- [Google Play Store policy and compliance](#google-play-store-policy-and-compliance)\n- [Manifest setup](#manifest-setup)\n- [Basic usage](#basic-usage)\n- [Customizing dialog theme](#customizing-dialog-theme)\n- [Changing dialog width and height](#changing-dialog-width-and-height)\n- [Complete Java example](#complete-java-example)\n- [DialogProperties reference](#dialogproperties-reference)\n- [DialogConfigs reference](#dialogconfigs-reference)\n- [Extension filtering](#extension-filtering)\n- [Single and multiple selection](#single-and-multiple-selection)\n- [Selecting directories](#selecting-directories)\n- [Pre-selecting files](#pre-selecting-files)\n- [Handling Android 11+ all-files access](#handling-android-11-all-files-access)\n- [Handling Android 13+ media permissions](#handling-android-13-media-permissions)\n- [Handling Android 14+ partial photo/video access](#handling-android-14-partial-photovideo-access)\n- [Play Store safe integration options](#play-store-safe-integration-options)\n- [Troubleshooting](#troubleshooting)\n- [Migration guide from v9.x to v10.1.3](#migration-guide-from-v9x-to-v1013)\n- [Security and privacy recommendations](#security-and-privacy-recommendations)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Overview\n\n`FilePicker` is an Android library that provides a simple UI for selecting:\n\n- Files\n- Directories\n- Files and directories together\n- Single item\n- Multiple items\n- Specific file extensions\n- Hidden files, when explicitly enabled\n\nThe library returns selected paths as a `String[]`.\n\n```java\ndialog.setDialogSelectionListener(files -\u003e {\n    for (String path : files) {\n        // Use selected path\n    }\n});\n```\n\n\u003e **Important:** This library is a **path-based file picker**. Android storage rules changed heavily from Android 10 onward. On Android 11+ full shared-storage browsing requires special handling, and for Google Play apps, `MANAGE_EXTERNAL_STORAGE` is allowed only for specific eligible app categories.\n\n---\n\n## What is new in v10\n\nVersion `10.X.X` focuses on modern Android support, stability, clearer storage behavior, dialog customization, and advanced file filtering.\n\n### New in v10.1.3\n\n- From `v10.1.3`, FilePicker supports checkbox color customization through `DialogProperties`.\n\n### New in v10.1.2\n\n- Added file size filtering support using `min_file_size` and `max_file_size`.\n- Added support for selecting files below a specific size.\n- Added support for selecting files above a specific size.\n- Added support for selecting files between a minimum and maximum size range.\n- Added support for combining file size filtering with extension filtering.\n- Added examples for selecting video files below a specific file size.\n- Added examples for selecting PDF files below a specific file size.\n- Added examples for selecting large files such as videos between 100 MB and 2 GB.\n- Added README documentation explaining that file size filtering works with all file types, not only videos.\n\n### New in v10.1.1\n\n- Added programmatic dialog width and height customization.\n- Added responsive percentage-based dialog sizing for tablets, foldables, landscape mode, and ultra-wide screens.\n- Added direct layout-param based sizing support using `ViewGroup.LayoutParams.MATCH_PARENT`, `WRAP_CONTENT`, or exact pixel values.\n- Documented the GitHub issue solution for changing dialog width and height before showing the picker.\n- Improved README examples for dialog theme and dialog size customization.\n\n### Core improvements\n\n- Updated default storage handling.\n- Improved Android 11+ behavior.\n- Improved Android 13+ permission guidance.\n- Added Android 14+ partial media access documentation.\n- Safer extension filtering.\n- Safer file size filtering.\n- Safer checkbox handling.\n- Safer selected item handling.\n- More predictable selected path ordering.\n- Better null checks for unreadable folders and invalid paths.\n- Better documentation for Play Store compliance.\n\n### Stability improvements\n\n- Prevents checkbox listener `NullPointerException`.\n- Prevents recycled-row checkbox state issues.\n- Handles unreadable directories safely.\n- Handles missing parent directories safely.\n- Handles invalid offset/root configuration safely.\n- Handles empty folders safely.\n- Handles extension formats like `pdf`, `.pdf`, and `*`.\n- Handles file size limits using `long` values to support large file sizes.\n\n---\n\n## Important Android storage reality\n\nAndroid does **not** allow every app to freely browse the full device storage on modern Android versions.\n\nThis library returns direct file paths. Because of this:\n\n- Android 6.0 to Android 10 can work with normal runtime storage permissions. Because this library now uses `minSdk 23`, runtime permission handling is required for every supported Android version below Android 11.\n- Android 11+ full shared-storage browsing needs **All files access** (`MANAGE_EXTERNAL_STORAGE`) if you want raw path access to many non-media files.\n- Android 13+ media permissions only cover images, videos, and audio.\n- Android 14+ can allow users to grant only selected photos/videos instead of full media access.\n- Google Play restricts `MANAGE_EXTERNAL_STORAGE` to apps where broad file access is the core functionality.\n\nIf your app is not a file manager, backup app, antivirus app, document-management app, or another Play-approved category, consider using Android's Storage Access Framework instead of asking for All files access.\n\nOfficial references:\n\n- Android all files access: https://developer.android.com/training/data-storage/manage-all-files\n- Google Play all files access policy: https://support.google.com/googleplay/android-developer/answer/10467955\n- Android 13 granular media permissions: https://developer.android.com/about/versions/13/behavior-changes-13\n- Android 14 selected photos access: https://developer.android.com/about/versions/14/changes/partial-photo-video-access\n\n---\n\n## Requirements\n\n| Requirement | Value |\n|---|---|\n| Minimum SDK | API 23+ / Android 6.0+ |\n| Recommended compile SDK | Latest stable Android SDK |\n| Language | Java compatible |\n| AndroidX | Required |\n| Supports Android 13+ | Yes, with correct media/all-files permission handling |\n| Supports Android 14+ | Yes, with partial media access awareness |\n| Returns | Direct file paths |\n| Best use case | File manager, document manager, backup/restore, local file tools, developer utilities |\n\n\u003e **minSdk note:** FilePicker v10.1.1 is documented for `minSdk 23`. Android 5.0 and 5.1 support has been removed from this README and should not be advertised in badges, Gradle metadata, or release notes.\n\n---\n\n## Installation\n\nFilePicker v10.1.3 is available through **Maven Central** and **JitPack**.\n\nRecommended installation method: **Maven Central**  \nAlternative installation method: **JitPack**\n\n\u003e Minimum supported SDK: **API 23 / Android 6.0+**\n\n---\n\n## Option 1: Installation using Maven Central\n\nMaven Central is the recommended way to use this library.\n\n### Step 1: Add Maven Central repository\n\nOpen your project-level `settings.gradle` file.\n\nUsually this file is located here:\n\n```text\nYourProject/settings.gradle\n````\n\nor, if you are using Kotlin DSL:\n\n```text\nYourProject/settings.gradle.kts\n```\n\nAdd `mavenCentral()` inside `dependencyResolutionManagement`.\n\n### Groovy DSL - settings.gradle\n\n```gradle\npluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.name = \"YourProjectName\"\ninclude ':app'\n```\n\nIf your project already has `dependencyResolutionManagement`, do not duplicate the full block. Just make sure `mavenCentral()` is added inside `repositories`.\n\nExample:\n\n```gradle\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n```\n\n### Kotlin DSL - settings.gradle.kts\n\n```kotlin\npluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.name = \"YourProjectName\"\ninclude(\":app\")\n```\n\n---\n\n### Step 2: Add FilePicker dependency\n\nOpen your app-level Gradle file.\n\nUsually this file is located here:\n\n```text\nYourProject/app/build.gradle\n```\n\nor, if you are using Kotlin DSL:\n\n```text\nYourProject/app/build.gradle.kts\n```\n\nAdd the FilePicker dependency inside the `dependencies` block.\n\n### Groovy DSL - app/build.gradle\n\n```gradle\ndependencies {\n    implementation \"io.github.tutorialsandroid:filepicker:10.1.3\"\n}\n```\n\n### Kotlin DSL - app/build.gradle.kts\n\n```kotlin\ndependencies {\n    implementation(\"io.github.tutorialsandroid:filepicker:10.1.3\")\n}\n```\n\n---\n\n### Step 3: Set minimum SDK to 23 or higher\n\nFilePicker v10.1.1 requires `minSdk 23` or higher.\n\nOpen your app-level Gradle file:\n\n```text\nYourProject/app/build.gradle\n```\n\nInside the `android` block, add or update `minSdk`.\n\n### Groovy DSL - app/build.gradle\n\n```gradle\nandroid {\n    namespace \"com.example.yourapp\"\n    compileSdk 35\n\n    defaultConfig {\n        applicationId \"com.example.yourapp\"\n        minSdk 23\n        targetSdk 35\n        versionCode 1\n        versionName \"1.0\"\n    }\n}\n```\n\n### Kotlin DSL - app/build.gradle.kts\n\n```kotlin\nandroid {\n    namespace = \"com.example.yourapp\"\n    compileSdk = 35\n\n    defaultConfig {\n        applicationId = \"com.example.yourapp\"\n        minSdk = 23\n        targetSdk = 35\n        versionCode = 1\n        versionName = \"1.0\"\n    }\n}\n```\n\nIf your project already has `minSdk 23` or higher, no change is required.\n\n---\n\n### Step 4: Sync the project\n\nAfter adding the dependency, click:\n\n```text\nSync Now\n```\n\nin Android Studio.\n\nYou can also sync manually from:\n\n```text\nFile \u003e Sync Project with Gradle Files\n```\n\n---\n\n## Option 2: Installation using JitPack\n\nJitPack support is also available for developers who prefer GitHub-based dependency installation.\n\n\u003e Maven Central is still recommended for production apps.\n\n---\n\n### Step 1: Add JitPack repository\n\nOpen your project-level `settings.gradle` file.\n\nUsually this file is located here:\n\n```text\nYourProject/settings.gradle\n```\n\nAdd `maven { url 'https://jitpack.io' }` inside `dependencyResolutionManagement`.\n\n### Groovy DSL - settings.gradle\n\n```gradle\npluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        maven { url 'https://jitpack.io' }\n    }\n}\n\nrootProject.name = \"YourProjectName\"\ninclude ':app'\n```\n\n### Kotlin DSL - settings.gradle.kts\n\n```kotlin\npluginManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        maven(\"https://jitpack.io\")\n    }\n}\n\nrootProject.name = \"YourProjectName\"\ninclude(\":app\")\n```\n\n---\n\n### Step 2: Add JitPack dependency\n\nOpen your app-level Gradle file:\n\n```text\nYourProject/app/build.gradle\n```\n\nAdd the dependency inside the `dependencies` block.\n\n### Groovy DSL - app/build.gradle\n\n```gradle\ndependencies {\n    implementation \"com.github.TutorialsAndroid:FilePicker:v10.1.3\"\n}\n```\n\n### Kotlin DSL - app/build.gradle.kts\n\n```kotlin\ndependencies {\n    implementation(\"com.github.TutorialsAndroid:FilePicker:v10.1.3\")\n}\n```\n\n---\n\n### Step 3: Set minimum SDK\n\nFilePicker v10.1.1 requires `minSdk 23` or higher.\n\n### Groovy DSL\n\n```gradle\nandroid {\n    defaultConfig {\n        minSdk 23\n    }\n}\n```\n\n### Kotlin DSL\n\n```kotlin\nandroid {\n    defaultConfig {\n        minSdk = 23\n    }\n}\n```\n\n---\n\n## Important Notes\n\n### Maven Central vs JitPack\n\nFor production apps, Maven Central is recommended:\n\n```gradle\nimplementation \"io.github.tutorialsandroid:filepicker:10.1.3\"\n```\n\nUse JitPack only if you specifically want to fetch the library directly from GitHub:\n\n```gradle\nimplementation \"com.github.TutorialsAndroid:FilePicker:v10.1.3\"\n```\n\n---\n\n### Do not add repositories inside app/build.gradle in modern Android projects\n\nOlder Android projects used this style:\n\n```gradle\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven { url 'https://jitpack.io' }\n    }\n}\n```\n\nFor modern Android Gradle Plugin versions, repositories should usually be added in:\n\n```text\nsettings.gradle\n```\n\ninside:\n\n```gradle\ndependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n```\n\nOnly use the old `allprojects` method if your project is very old and does not have `dependencyResolutionManagement`.\n\n---\n\n## Installation\n\n### Maven Central\n\n```gradle\ndependencies {\n    implementation \"io.github.tutorialsandroid:filepicker:10.1.3\"\n}\n```\n\n### JitPack\n\n```gradle\ndependencies {\n    implementation \"com.github.TutorialsAndroid:FilePicker:v10.1.3\"\n}\n```\n\n### Minimum SDK\n\n```gradle\nandroid {\n    defaultConfig {\n        minSdk 23\n    }\n}\n```\n\nSmall Note: use **one dependency only** in your project. Do not add both Maven Central and JitPack dependency at the same time, otherwise Gradle may pull duplicate versions.\n\n---\n\n## Permissions by Android version\n\n### Quick permission table\n\n| Android version | API | Files/images/videos/audio behavior | Recommended approach |\n|---|---:|---|---|\n| Android 6.0 - 9 | 23-28 | Runtime `READ_EXTERNAL_STORAGE` / limited legacy write behavior | Request runtime read permission; request write only if your app truly writes to shared storage on older Android |\n| Android 10 | 29 | Scoped storage introduced; legacy flag can help old path-based apps | Use `requestLegacyExternalStorage=\"true\"` only when needed |\n| Android 11 - 12L | 30-32 | Scoped storage enforced; broad path access requires All files access | Use SAF/MediaStore, or `MANAGE_EXTERNAL_STORAGE` only if eligible |\n| Android 13 | 33 | `READ_EXTERNAL_STORAGE` replaced for media by granular media permissions | Use `READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`, `READ_MEDIA_AUDIO`; use All files access only if eligible |\n| Android 14+ | 34+ | Partial photo/video access introduced | Handle selected media access; use SAF/MediaStore when possible |\n\n---\n\n## Google Play Store policy and compliance\n\nThis section is very important if you publish an app using this library on Google Play.\n\n### 1. Do not blindly add `MANAGE_EXTERNAL_STORAGE`\n\n`MANAGE_EXTERNAL_STORAGE` is a high-risk permission. Google Play allows it only when broad file access is required for the app's **core functionality**.\n\nUsually permitted categories include apps such as:\n\n- File managers\n- Backup and restore apps\n- Antivirus apps\n- Document management apps\n- Device migration apps\n- Search/indexing tools where broad storage access is central\n- Disk/file cleanup tools, if the policy requirements are satisfied\n\nUsually rejected or risky cases include:\n\n- Normal gallery apps that can use MediaStore\n- Chat apps that only attach selected files\n- Apps that only upload one selected document\n- Apps that only need a user-selected folder\n- Apps that can use Android Photo Picker, SAF, or MediaStore\n- Apps that request All files access only for convenience\n\n### 2. Complete the Play Console Permissions Declaration Form\n\nIf your app declares `MANAGE_EXTERNAL_STORAGE`, you may need to complete the Permissions Declaration Form in Play Console and explain:\n\n- Why your app needs broad file access.\n- Why SAF or MediaStore is not enough.\n- What user-facing core feature depends on it.\n- How users benefit from this access.\n- How you protect user data.\n\n### 3. Keep the permission off if not needed\n\nIf your app does not truly need full shared-storage browsing, remove this permission:\n\n```xml\n\u003cuses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\" /\u003e\n```\n\nUse one of these alternatives:\n\n- Android Photo Picker for user-selected images/videos.\n- Storage Access Framework for user-selected documents/folders.\n- MediaStore for images, videos, and audio.\n- App-specific storage for private app files.\n\n### 4. Privacy policy and Data safety\n\nIf your app accesses user files, your privacy policy should clearly explain:\n\n- What types of files are accessed.\n- Whether files are uploaded or processed locally.\n- Whether file paths, file names, or metadata are collected.\n- Whether files are shared with third parties.\n- How users can delete their data.\n- Why the permission is necessary.\n\nIn Play Console Data safety, declare file/media access truthfully.\n\n### 5. Recommended Play Store wording\n\nUse a simple user-facing explanation:\n\n\u003e This app needs file access so you can browse, select, manage, and organize files stored on your device. The app does not access files in the background and only uses selected files for user-requested actions.\n\nFor All files access:\n\n\u003e All files access is required because the app's core file manager feature lets users browse, search, select, and manage documents and folders across shared storage. Without this access, the core file-management feature cannot function correctly.\n\nDo not claim you need All files access if your app only selects one file occasionally.\n\n---\n\n## Manifest setup\n\nUse only the permissions required by your app. Do not copy every permission blindly.\n\n### Full path-based file picker manifest\n\nUse this only if your app is eligible for broad file access.\n\n```xml\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\u003e\n\n    \u003c!-- Android 6.0 to Android 12L read access. minSdk is 23, so no API 21/22 handling is required. --\u003e\n    \u003cuses-permission\n        android:name=\"android.permission.READ_EXTERNAL_STORAGE\"\n        android:maxSdkVersion=\"32\" /\u003e\n\n    \u003c!-- Write permission is meaningful only for older Android versions --\u003e\n    \u003cuses-permission\n        android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n        android:maxSdkVersion=\"28\" /\u003e\n\n    \u003c!-- Android 13+ media permissions --\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" /\u003e\n\n    \u003c!-- Android 14+ selected photo/video access awareness --\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_VISUAL_USER_SELECTED\" /\u003e\n\n    \u003c!-- Android 11+ broad shared-storage access.\n         Use only if your app is eligible under Google Play policy. --\u003e\n    \u003cuses-permission\n        android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"\n        tools:ignore=\"ScopedStorage\" /\u003e\n\n    \u003capplication\n        android:theme=\"@style/AppTheme\"\n        android:requestLegacyExternalStorage=\"true\"\u003e\n        ...\n    \u003c/application\u003e\n\n\u003c/manifest\u003e\n```\n\n### Play Store safer manifest\n\nFor apps that only allow users to pick a document or media item occasionally, do not use `MANAGE_EXTERNAL_STORAGE`.\n\n```xml\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\u003e\n\n    \u003cuses-permission\n        android:name=\"android.permission.READ_EXTERNAL_STORAGE\"\n        android:maxSdkVersion=\"32\" /\u003e\n\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" /\u003e\n\n    \u003capplication\n        android:theme=\"@style/AppTheme\"\u003e\n        ...\n    \u003c/application\u003e\n\n\u003c/manifest\u003e\n```\n\n---\n\n## Basic usage\n\n### 1. Create `DialogProperties`\n\n```java\nDialogProperties properties = new DialogProperties();\n```\n\n### 2. Configure picker behavior\n\n```java\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\nproperties.root = new File(DialogConfigs.DEFAULT_DIR);\nproperties.offset = new File(DialogConfigs.DEFAULT_DIR);\nproperties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\nproperties.extensions = null;\nproperties.show_hidden_files = false;\n```\n\n### 3. Create dialog\n\n```java\nFilePickerDialog dialog = new FilePickerDialog(this, properties);\ndialog.setTitle(\"Select a File\");\ndialog.setPositiveBtnName(\"Select\");\ndialog.setNegativeBtnName(\"Cancel\");\n```\n\n### 4. Receive selected paths\n\n```java\ndialog.setDialogSelectionListener(files -\u003e {\n    for (String path : files) {\n        File file = new File(path);\n        // Use the selected file\n    }\n});\n```\n\n### 5. Show dialog\n\n```java\ndialog.show();\n```\n\n\u003e On Android 11+, make sure required storage access is already granted before calling `dialog.show()`.\n\n---\n\n## Customizing dialog theme\n\nThis section answers a common GitHub issue/question: **\"How to change Dialog theme?\"**\n\n`FilePickerDialog` supports custom dialog themes through the constructor that accepts `themeResId`:\n\n```java\nFilePickerDialog dialog = new FilePickerDialog(\n        MainActivity.this,\n        properties,\n        R.style.YourCustomFilePickerTheme\n);\n```\n\nUse this constructor when you want to change the dialog colors, typography, shape, background, or Material/AppCompat dialog styling.\n\n### Complete themed dialog example\n\n```java\nDialogProperties properties = new DialogProperties();\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\nproperties.root = new File(DialogConfigs.DEFAULT_DIR);\nproperties.offset = new File(DialogConfigs.DEFAULT_DIR);\nproperties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\nproperties.extensions = null;\nproperties.show_hidden_files = false;\n\nFilePickerDialog dialog = new FilePickerDialog(\n        MainActivity.this,\n        properties,\n        R.style.YourCustomFilePickerTheme\n);\n\ndialog.setTitle(\"Select a File\");\ndialog.setPositiveBtnName(\"Select\");\ndialog.setNegativeBtnName(\"Cancel\");\n\ndialog.setDialogSelectionListener(files -\u003e {\n    for (String path : files) {\n        // Use selected path\n    }\n});\n\ndialog.show();\n```\n\n### Example theme using AppCompat\n\nAdd this in `res/values/styles.xml`:\n\n```xml\n\u003cstyle name=\"YourCustomFilePickerTheme\" parent=\"Theme.AppCompat.Light.Dialog.Alert\"\u003e\n    \u003citem name=\"colorPrimary\"\u003e#6750A4\u003c/item\u003e\n    \u003citem name=\"colorPrimaryDark\"\u003e#4F378B\u003c/item\u003e\n    \u003citem name=\"colorAccent\"\u003e#6750A4\u003c/item\u003e\n    \u003citem name=\"android:fontFamily\"\u003esans\u003c/item\u003e\n\u003c/style\u003e\n```\n\n### Example theme using Material Components\n\nUse this only if your app already depends on Material Components and your app theme is Material-compatible.\n\n```xml\n\u003cstyle name=\"YourCustomFilePickerTheme\" parent=\"Theme.MaterialComponents.DayNight.Dialog.Alert\"\u003e\n    \u003citem name=\"colorPrimary\"\u003e#6750A4\u003c/item\u003e\n    \u003citem name=\"colorPrimaryVariant\"\u003e#4F378B\u003c/item\u003e\n    \u003citem name=\"colorSecondary\"\u003e#6750A4\u003c/item\u003e\n    \u003citem name=\"colorAccent\"\u003e#6750A4\u003c/item\u003e\n    \u003citem name=\"android:fontFamily\"\u003esans\u003c/item\u003e\n\u003c/style\u003e\n```\n\n### Dark theme example\n\n```xml\n\u003cstyle name=\"YourDarkFilePickerTheme\" parent=\"Theme.AppCompat.Dialog.Alert\"\u003e\n    \u003citem name=\"android:windowBackground\"\u003e#121212\u003c/item\u003e\n    \u003citem name=\"android:textColorPrimary\"\u003e#FFFFFF\u003c/item\u003e\n    \u003citem name=\"android:textColorSecondary\"\u003e#BDBDBD\u003c/item\u003e\n    \u003citem name=\"colorPrimary\"\u003e#BB86FC\u003c/item\u003e\n    \u003citem name=\"colorAccent\"\u003e#BB86FC\u003c/item\u003e\n\u003c/style\u003e\n```\n\nThen use it:\n\n```java\nFilePickerDialog dialog = new FilePickerDialog(\n        MainActivity.this,\n        properties,\n        R.style.YourDarkFilePickerTheme\n);\n```\n\n### Important theming notes\n\n- The theme must be a dialog-compatible theme.\n- If you use a Material Components parent theme, your app should include Material Components and use a Material-compatible application theme.\n- `setTitle()`, `setPositiveBtnName()`, and `setNegativeBtnName()` only change text, not the full dialog style.\n- Button and checkbox colors are controlled mainly by `colorAccent` and related theme color attributes.\n- For deep UI changes such as row layout, icon size, text sizes, or spacing, customize the library layout resources in your app or fork the library.\n- Do not use a full-screen activity theme as the dialog theme unless you intentionally want full-screen behavior.\n\n---\n\n## Customizing checkbox colors\n\nFrom `v10.1.3`, FilePicker supports checkbox color customization through `DialogProperties`.\n\nThis is useful when the default checkbox colors do not match your app theme, or when the unchecked checkbox blends into a white/light background.\n\n```java\nimport android.graphics.Color;\n\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.MULTI_MODE;\nproperties.selection_type = DialogConfigs.DIR_SELECT;\n\n// Checked checkbox fill color\nproperties.checkbox_checked_color = Color.parseColor(\"#6750A4\");\n\n// Unchecked checkbox outer/background color\nproperties.checkbox_unchecked_color = Color.parseColor(\"#6750A4\");\n\n// Tick/checkmark color shown when checked\nproperties.checkbox_checkmark_color = Color.WHITE;\n\n// Inner color shown when unchecked\nproperties.checkbox_unchecked_inner_color = Color.parseColor(\"#F3E8FF\");\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select Directory\");\ndialog.show();\n```\n\n### Notes\n\n- `checkbox_checked_color` controls the selected checkbox fill color.\n- `checkbox_unchecked_color` controls the unchecked checkbox outer/background color.\n- `checkbox_checkmark_color` controls the tick/checkmark color.\n- `checkbox_unchecked_inner_color` controls the inner unchecked color.\n- If `checkbox_checked_color` is not set, FilePicker uses the existing `R.color.colorAccent` fallback.\n- This works for file selection, directory selection, and file + directory selection.\n\n## Changing dialog width and height\n\nThis section answers a common GitHub issue/question: **\"Change Width and Height\"**.\n\n`FilePickerDialog` supports programmatic dialog sizing. This is useful for:\n\n- Ultra-wide screens\n- Landscape mode\n- Tablets\n- Foldables\n- ChromeOS devices\n- Custom kiosk-style layouts\n- Apps that need a wider picker UI than the default Android dialog width\n\nYou should call the size method **before** `dialog.show()`.\n\n### Set size using screen percentage\n\nUse this when you want responsive sizing across different screen sizes.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\n\ndialog.setTitle(\"Select File\");\ndialog.setPositiveBtnName(\"Select\");\ndialog.setNegativeBtnName(\"Cancel\");\n\n// 85% of screen width and 75% of screen height\ndialog.setDialogSizeByPercent(0.85f, 0.75f);\n\ndialog.show();\n```\n\n### Set width only and keep default height\n\n```java\ndialog.setDialogSizeByPercent(0.90f, -1f);\ndialog.show();\n```\n\n### Set height only and keep default width\n\n```java\ndialog.setDialogSizeByPercent(-1f, 0.75f);\ndialog.show();\n```\n\n### Use direct layout params\n\nUse this when you want exact Android layout behavior.\n\n```java\ndialog.setDialogSize(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        ViewGroup.LayoutParams.WRAP_CONTENT\n);\n\ndialog.show();\n```\n\nRequired import:\n\n```java\nimport android.view.ViewGroup;\n```\n\n### Use exact pixel values\n\n```java\ndialog.setDialogSize(1200, 700);\ndialog.show();\n```\n\n\u003e Pixel values are not recommended for normal apps because they may look different on different screen densities. Prefer `setDialogSizeByPercent()` for responsive UI.\n\n### Complete width/height example\n\n```java\nDialogProperties properties = new DialogProperties();\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\nproperties.root = new File(DialogConfigs.DEFAULT_DIR);\nproperties.offset = new File(DialogConfigs.DEFAULT_DIR);\nproperties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\nproperties.extensions = null;\nproperties.show_hidden_files = false;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\n\ndialog.setTitle(\"Select a File\");\ndialog.setPositiveBtnName(\"Select\");\ndialog.setNegativeBtnName(\"Cancel\");\n\n// Makes the dialog wider on landscape, tablet, foldable, and ultra-wide displays.\ndialog.setDialogSizeByPercent(0.85f, 0.75f);\n\ndialog.setDialogSelectionListener(files -\u003e {\n    for (String path : files) {\n        // Use selected path\n    }\n});\n\ndialog.show();\n```\n\n### Dialog size API\n\n| Method | Description |\n|---|---|\n| `setDialogSize(int width, int height)` | Sets dialog width and height using Android layout params or exact pixel values |\n| `setDialogSizeByPercent(float widthPercent, float heightPercent)` | Sets dialog width and height using screen percentage |\n| `ViewGroup.LayoutParams.MATCH_PARENT` | Makes the dialog use maximum available width/height |\n| `ViewGroup.LayoutParams.WRAP_CONTENT` | Keeps default content-based size |\n| `-1f` in percent method | Keeps that dimension unchanged/default |\n\n### Recommended values\n\n| Use case | Recommended value |\n|---|---|\n| Normal phones | Default dialog size |\n| Landscape phones | `setDialogSizeByPercent(0.90f, 0.80f)` |\n| Tablets | `setDialogSizeByPercent(0.75f, 0.80f)` |\n| Ultra-wide screens | `setDialogSizeByPercent(0.85f, 0.75f)` |\n| Full-width picker | `setDialogSize(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)` |\n\n### Important notes\n\n- Call `setDialogSize()` or `setDialogSizeByPercent()` before `dialog.show()`.\n- If called after `dialog.show()`, the dialog should update immediately.\n- Use percentage-based sizing for responsive UI.\n- Use direct layout params only when you need exact Android layout behavior.\n- Avoid hardcoded pixels unless you are building for a fixed-size device.\n- Dialog sizing changes the outer dialog window size, not the internal row layout.\n- For deeper layout changes such as row height, icon size, padding, or text size, customize the library XML layout resources or fork the UI layer.\n\n\n---\n\n## Complete Java example\n\nThis sample uses modern `ActivityResultLauncher` APIs instead of deprecated `startActivityForResult()`.\n\n```java\npackage com.example.filepickerdemo;\n\nimport static android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION;\n\nimport android.Manifest;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.provider.Settings;\nimport android.widget.Button;\nimport android.widget.Toast;\n\nimport androidx.activity.result.ActivityResultLauncher;\nimport androidx.activity.result.contract.ActivityResultContracts;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.content.ContextCompat;\n\nimport com.developer.filepicker.model.DialogConfigs;\nimport com.developer.filepicker.model.DialogProperties;\nimport com.developer.filepicker.view.FilePickerDialog;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MainActivity extends AppCompatActivity {\n\n    private FilePickerDialog filePickerDialog;\n\n    private ActivityResultLauncher\u003cString[]\u003e permissionLauncher;\n    private ActivityResultLauncher\u003cIntent\u003e allFilesAccessLauncher;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        setupPermissionLaunchers();\n        setupFilePicker();\n\n        Button showDialog = findViewById(R.id.show_dialog);\n        showDialog.setOnClickListener(view -\u003e openPickerSafely());\n    }\n\n    private void setupFilePicker() {\n        DialogProperties properties = new DialogProperties();\n\n        properties.selection_mode = DialogConfigs.SINGLE_MODE;\n        properties.selection_type = DialogConfigs.FILE_SELECT;\n        properties.root = new File(DialogConfigs.DEFAULT_DIR);\n        properties.offset = new File(DialogConfigs.DEFAULT_DIR);\n        properties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\n\n        // null = show all allowed files\n        properties.extensions = null;\n\n        // false = hide dot files\n        properties.show_hidden_files = false;\n\n        filePickerDialog = new FilePickerDialog(this, properties);\n        filePickerDialog.setTitle(\"Select a File\");\n        filePickerDialog.setPositiveBtnName(\"Select\");\n        filePickerDialog.setNegativeBtnName(\"Cancel\");\n\n        // Optional v10.1.1 feature:\n        // Make the dialog wider/taller on landscape, tablet, foldable, or ultra-wide screens.\n        filePickerDialog.setDialogSizeByPercent(0.85f, 0.75f);\n\n        filePickerDialog.setDialogSelectionListener(files -\u003e {\n            for (String path : files) {\n                Toast.makeText(this, \"Selected: \" + path, Toast.LENGTH_SHORT).show();\n            }\n        });\n    }\n\n    private void setupPermissionLaunchers() {\n        permissionLauncher = registerForActivityResult(\n                new ActivityResultContracts.RequestMultiplePermissions(),\n                result -\u003e {\n                    if (hasRequiredAccess()) {\n                        filePickerDialog.show();\n                    } else {\n                        showPermissionDeniedMessage();\n                    }\n                }\n        );\n\n        allFilesAccessLauncher = registerForActivityResult(\n                new ActivityResultContracts.StartActivityForResult(),\n                result -\u003e {\n                    if (hasRequiredAccess()) {\n                        filePickerDialog.show();\n                    } else {\n                        showPermissionDeniedMessage();\n                    }\n                }\n        );\n    }\n\n    private void openPickerSafely() {\n        if (hasRequiredAccess()) {\n            filePickerDialog.show();\n            return;\n        }\n\n        if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.R) {\n            showAllFilesAccessDialog();\n        } else {\n            requestLegacyStoragePermissions();\n        }\n    }\n\n    private boolean hasRequiredAccess() {\n        if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.R) {\n            return Environment.isExternalStorageManager();\n        }\n\n        if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.M) {\n            return ContextCompat.checkSelfPermission(\n                    this,\n                    Manifest.permission.READ_EXTERNAL_STORAGE\n            ) == PackageManager.PERMISSION_GRANTED;\n        }\n\n        return true;\n    }\n\n    private void requestLegacyStoragePermissions() {\n        if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.M) {\n            permissionLauncher.launch(new String[]{\n                    Manifest.permission.READ_EXTERNAL_STORAGE\n            });\n        }\n    }\n\n    private void showAllFilesAccessDialog() {\n        new AlertDialog.Builder(this)\n                .setTitle(\"Storage Permission Required\")\n                .setMessage(\"To browse all files and folders, please allow All files access for this app. Use this only if broad file access is required for your app's main feature.\")\n                .setPositiveButton(\"Open Settings\", (dialog, which) -\u003e openAllFilesAccessSettings())\n                .setNegativeButton(\"Cancel\", null)\n                .show();\n    }\n\n    private void openAllFilesAccessSettings() {\n        if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.R) {\n            try {\n                Intent intent = new Intent(\n                        ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,\n                        Uri.parse(\"package:\" + getPackageName())\n                );\n                allFilesAccessLauncher.launch(intent);\n            } catch (Exception exception) {\n                Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);\n                allFilesAccessLauncher.launch(intent);\n            }\n        }\n    }\n\n    private void showPermissionDeniedMessage() {\n        Toast.makeText(\n                this,\n                \"Storage permission is required to browse files.\",\n                Toast.LENGTH_LONG\n        ).show();\n    }\n}\n```\n\n---\n\n## DialogProperties reference\n\n`DialogProperties` controls how the picker behaves.\n\n```java\nDialogProperties properties = new DialogProperties();\n```\n\n| Property | Type | Default | Description |\n|---|---|---|---|\n| `selection_mode` | `int` | `DialogConfigs.SINGLE_MODE` | Single or multiple selection |\n| `selection_type` | `int` | `DialogConfigs.FILE_SELECT` | Select files, directories, or both |\n| `root` | `File` | `DialogConfigs.DEFAULT_DIR` | Highest accessible directory for picker navigation |\n| `offset` | `File` | `DialogConfigs.DEFAULT_DIR` | Initial directory opened when dialog starts |\n| `error_dir` | `File` | `DialogConfigs.DEFAULT_DIR` | Fallback directory when root/offset is invalid |\n| `extensions` | `String[]` | `null` | Allowed file extensions |\n| `show_hidden_files` | `boolean` | `false` | Whether hidden dot-files are visible |\n\n### Recommended defaults\n\n```java\nDialogProperties properties = new DialogProperties();\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\nproperties.root = new File(DialogConfigs.DEFAULT_DIR);\nproperties.offset = new File(DialogConfigs.DEFAULT_DIR);\nproperties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\nproperties.extensions = null;\nproperties.show_hidden_files = false;\n```\n\n### Root, offset, and error directory rules\n\n- `root` should be an existing readable directory.\n- `offset` should be inside `root`.\n- `error_dir` should be an existing readable fallback directory.\n- Do not pass file paths as `root`, `offset`, or `error_dir`.\n- Avoid hardcoded `/sdcard` paths in new apps.\n- Prefer `DialogConfigs.DEFAULT_DIR` or `Environment.getExternalStorageDirectory()`.\n\n---\n\n## DialogConfigs reference\n\n### Selection mode\n\n```java\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\n```\n\n| Constant | Description |\n|---|---|\n| `DialogConfigs.SINGLE_MODE` | User can select only one item |\n| `DialogConfigs.MULTI_MODE` | User can select multiple items |\n\n### Selection type\n\n```java\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n```\n\n| Constant | Description |\n|---|---|\n| `DialogConfigs.FILE_SELECT` | Files only |\n| `DialogConfigs.DIR_SELECT` | Directories only |\n| `DialogConfigs.FILE_AND_DIR_SELECT` | Files and directories |\n\n---\n\n## Extension filtering\n\n### Show all files\n\n```java\nproperties.extensions = null;\n```\n\n### Show only selected extensions\n\n```java\nproperties.extensions = new String[]{\"pdf\", \"docx\", \"xlsx\"};\n```\n\n### Dot prefix is also supported in v10\n\n```java\nproperties.extensions = new String[]{\".pdf\", \".docx\", \".xlsx\"};\n```\n\n### Wildcard\n\n```java\nproperties.extensions = new String[]{\"*\"};\n```\n\n### Best practices\n\n- Use lowercase extensions where possible.\n- Do not include MIME types here.\n- Use `pdf`, not `application/pdf`.\n- Directories remain visible if readable, because users need to navigate through folders.\n- If `selection_type` is `DIR_SELECT`, files are not selectable.\n\n--- \n\n## File size filtering\n\nFilePicker supports file size filtering using `min_file_size` and `max_file_size`.\n\nThis feature is not limited to video files. It works with **any file type** because the picker checks the actual file size in bytes.\n\nYou can use file size filtering for:\n\n- Videos such as `mp4`, `mkv`, `avi`, `mov`\n- Documents such as `pdf`, `doc`, `docx`, `xls`, `xlsx`\n- Images such as `jpg`, `jpeg`, `png`, `webp`\n- Audio files such as `mp3`, `wav`, `aac`\n- Archives such as `zip`, `rar`, `7z`\n- Any other readable file type\n\nFile size filtering can be used in three ways:\n\n1. Allow files below a specific size.\n2. Allow files above a specific size.\n3. Allow files between a minimum and maximum size.\n\n---\n\n### File size values are in bytes\n\n`min_file_size` and `max_file_size` accept values in **bytes**.\n\n```java\nproperties.min_file_size = 5L * 1024L * 1024L;   // 5 MB\nproperties.max_file_size = 50L * 1024L * 1024L;  // 50 MB\n```\n\nThe `L` is important because file sizes can become large. It tells Java to calculate the value as a `long`.\n\n---\n\n### Disable file size filtering\n\nBy default, file size filtering is disabled.\n\n```java\nproperties.min_file_size = -1L;\nproperties.max_file_size = -1L;\n```\n\n`-1L` means there is no limit.\n\n---\n\n### Select any file below 50 MB\n\nThis example allows the user to select any readable file below 50 MB.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// null means all file extensions are allowed\nproperties.extensions = null;\n\n// Allow files up to 50 MB\nproperties.max_file_size = 50L * 1024L * 1024L;\n\n// No minimum size limit\nproperties.min_file_size = -1L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select File Below 50 MB\");\ndialog.show();\n```\n\n---\n\n### Select video files below 100 MB\n\nThis example allows only video files below 100 MB.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// Only video files\nproperties.extensions = new String[]{\n        \"mp4\",\n        \"mkv\",\n        \"avi\",\n        \"mov\",\n        \"webm\",\n        \"3gp\"\n};\n\n// Allow videos up to 100 MB\nproperties.max_file_size = 100L * 1024L * 1024L;\n\n// No minimum size limit\nproperties.min_file_size = -1L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select Video Below 100 MB\");\ndialog.show();\n```\n\n---\n\n### Select PDF files below 10 MB\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// Only PDF files\nproperties.extensions = new String[]{\"pdf\"};\n\n// Allow PDF files up to 10 MB\nproperties.max_file_size = 10L * 1024L * 1024L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select PDF Below 10 MB\");\ndialog.show();\n```\n\n---\n\n### Select files larger than 20 MB\n\nYou can also allow only files larger than a specific size by using `min_file_size`.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// Allow all file types\nproperties.extensions = null;\n\n// Only show files larger than 20 MB\nproperties.min_file_size = 20L * 1024L * 1024L;\n\n// No maximum size limit\nproperties.max_file_size = -1L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select File Larger Than 20 MB\");\ndialog.show();\n```\n\n---\n\n### Select files between 5 MB and 200 MB\n\nThis example allows any file type between 5 MB and 200 MB.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// Allow all file types\nproperties.extensions = null;\n\n// Minimum size: 5 MB\nproperties.min_file_size = 5L * 1024L * 1024L;\n\n// Maximum size: 200 MB\nproperties.max_file_size = 200L * 1024L * 1024L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select File Between 5 MB and 200 MB\");\ndialog.show();\n```\n\n---\n\n### Select large video files between 100 MB and 2 GB\n\nFilePicker also supports large file size limits because `min_file_size` and `max_file_size` use `long`.\n\n```java\nDialogProperties properties = new DialogProperties();\n\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n\n// Only video files\nproperties.extensions = new String[]{\n        \"mp4\",\n        \"mkv\",\n        \"avi\",\n        \"mov\",\n        \"webm\"\n};\n\n// Minimum size: 100 MB\nproperties.min_file_size = 100L * 1024L * 1024L;\n\n// Maximum size: 2 GB\nproperties.max_file_size = 2L * 1024L * 1024L * 1024L;\n\nFilePickerDialog dialog = new FilePickerDialog(MainActivity.this, properties);\ndialog.setTitle(\"Select Large Video File\");\ndialog.show();\n```\n\n---\n\n### Helper method for converting MB to bytes\n\nFor cleaner code, you can create a helper method:\n\n```java\nprivate long mbToBytes(long mb) {\n    return mb * 1024L * 1024L;\n}\n```\n\nUsage:\n\n```java\nproperties.max_file_size = mbToBytes(50);   // 50 MB\nproperties.min_file_size = mbToBytes(5);    // 5 MB\n```\n\n---\n\n### Helper method for converting GB to bytes\n\n```java\nprivate long gbToBytes(long gb) {\n    return gb * 1024L * 1024L * 1024L;\n}\n```\n\nUsage:\n\n```java\nproperties.max_file_size = gbToBytes(2);    // 2 GB\n```\n\n---\n\n### Important notes\n\n- File size values must be provided in bytes.\n- Use `L` in size calculations to avoid integer overflow.\n- `-1L` means no limit.\n- `min_file_size` checks the minimum allowed file size.\n- `max_file_size` checks the maximum allowed file size.\n- File size filtering works with all file types, not only videos.\n- Extension filtering and file size filtering can be combined.\n- Directories are not blocked by file size filters because users still need to browse folders.\n- File size filtering only applies to files.\n- If both `min_file_size` and `max_file_size` are set, the file must be inside that size range.\n- If `min_file_size` is greater than `max_file_size`, no file may match your filter, so validate your values before showing the dialog.\n- On Android 11 and above, storage access rules still apply. File size filtering does not bypass Android scoped storage or Play Store permission policies.\n\n---\n\n## Single and multiple selection\n\n### Single file selection\n\n```java\nproperties.selection_mode = DialogConfigs.SINGLE_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n```\n\n### Multiple file selection\n\n```java\nproperties.selection_mode = DialogConfigs.MULTI_MODE;\nproperties.selection_type = DialogConfigs.FILE_SELECT;\n```\n\n### Multiple files and folders\n\n```java\nproperties.selection_mode = DialogConfigs.MULTI_MODE;\nproperties.selection_type = DialogConfigs.FILE_AND_DIR_SELECT;\n```\n\n---\n\n## Selecting directories\n\nTo allow directory selection:\n\n```java\nproperties.selection_type = DialogConfigs.DIR_SELECT;\n```\n\nTo allow both files and directories:\n\n```java\nproperties.selection_type = DialogConfigs.FILE_AND_DIR_SELECT;\n```\n\n---\n\n## Pre-selecting files\n\nYou can mark files before opening the dialog.\n\n```java\nList\u003cString\u003e selectedPaths = new ArrayList\u003c\u003e();\nselectedPaths.add(\"/storage/emulated/0/Download/example.pdf\");\n\ndialog.markFiles(selectedPaths);\n```\n\nRules:\n\n- In `SINGLE_MODE`, only the first valid path is used.\n- In `MULTI_MODE`, all valid paths are used.\n- Invalid paths are ignored.\n- Selection type rules are respected.\n\n---\n\n## Handling Android 11+ all-files access\n\nAndroid 11 introduced stricter scoped storage enforcement. If your app truly needs direct path access across shared storage, check All files access before showing the picker.\n\n```java\nif (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.R) {\n    if (Environment.isExternalStorageManager()) {\n        dialog.show();\n    } else {\n        // Redirect user to All files access settings\n    }\n}\n```\n\nOpen settings:\n\n```java\nIntent intent = new Intent(\n        Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,\n        Uri.parse(\"package:\" + getPackageName())\n);\nstartActivity(intent);\n```\n\nFallback:\n\n```java\nIntent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);\nstartActivity(intent);\n```\n\n### Important warning\n\nOnly request All files access if your app's core functionality requires it. Google Play may reject apps that request this permission without a policy-approved reason.\n\n---\n\n## Handling Android 13+ media permissions\n\nAndroid 13 introduced granular media permissions.\n\n```xml\n\u003cuses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" /\u003e\n\u003cuses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" /\u003e\n\u003cuses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" /\u003e\n```\n\nUse them based on what your app needs.\n\n```java\nif (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.TIRAMISU) {\n    permissionLauncher.launch(new String[]{\n            Manifest.permission.READ_MEDIA_IMAGES,\n            Manifest.permission.READ_MEDIA_VIDEO,\n            Manifest.permission.READ_MEDIA_AUDIO\n    });\n}\n```\n\n### Important\n\nThese permissions are only for media files:\n\n- Images\n- Videos\n- Audio\n\nThey do not provide complete access to:\n\n- PDF\n- ZIP\n- TXT\n- DOC/DOCX\n- XLS/XLSX\n- APK\n- Arbitrary folders\n\nFor those files on Android 11+, use SAF or eligible All files access.\n\n---\n\n## Handling Android 14+ partial photo/video access\n\nAndroid 14 allows users to grant access only to selected photos/videos.\n\nAdd this permission if your app handles visual media selection behavior on Android 14+:\n\n```xml\n\u003cuses-permission android:name=\"android.permission.READ_MEDIA_VISUAL_USER_SELECTED\" /\u003e\n```\n\nImportant notes:\n\n- Users may grant partial access instead of full media library access.\n- Your app should not assume it can see every image or video.\n- For broad file browsing, this does not replace All files access.\n- For media-only apps, consider Android Photo Picker.\n\n---\n\n## Play Store safe integration options\n\n### Option A: File manager/document manager app\n\nUse this library with `MANAGE_EXTERNAL_STORAGE` only if broad file access is your main feature.\n\nRecommended for:\n\n- File manager\n- Document manager\n- Backup/restore\n- Device migration\n- Antivirus/file scanning\n- File search/indexing\n\nYou must:\n\n- Explain the permission clearly to users.\n- Complete Play Console permission declaration.\n- Maintain a privacy policy.\n- Avoid accessing files in the background without user action.\n- Avoid uploading files without explicit user consent.\n\n### Option B: Normal app that only needs user-selected files\n\nDo not use All files access.\n\nUse:\n\n- Storage Access Framework\n- Android Photo Picker\n- MediaStore\n\nRecommended for:\n\n- Chat attachment picker\n- Profile photo picker\n- Upload document feature\n- Import/export one file\n- Select a backup file manually\n\n### Option C: Internal/private/distribution outside Play Store\n\nIf your APK is used internally, enterprise-side-loaded, or outside Google Play, you can use All files access depending on your distribution policy and user consent. Still explain clearly why the permission is needed.\n\n---\n\n## Troubleshooting\n\n### Dialog looks too small on tablets, landscape, or ultra-wide screens\n\nUse the dialog sizing API before showing the picker:\n\n```java\ndialog.setDialogSizeByPercent(0.85f, 0.75f);\ndialog.show();\n```\n\nFor full-width behavior:\n\n```java\ndialog.setDialogSize(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        ViewGroup.LayoutParams.WRAP_CONTENT\n);\n```\n\n### Dialog opens but folder is empty\n\nPossible causes:\n\n- Permission not granted.\n- Android 11+ All files access not granted.\n- Root path is invalid.\n- Offset path is outside root.\n- Folder is unreadable.\n- Extension filter hides files.\n\nFix:\n\n```java\nproperties.extensions = null;\nproperties.root = new File(DialogConfigs.DEFAULT_DIR);\nproperties.offset = new File(DialogConfigs.DEFAULT_DIR);\nproperties.error_dir = new File(DialogConfigs.DEFAULT_DIR);\n```\n\n### PDF/DOC/ZIP files not visible on Android 13+\n\n`READ_MEDIA_*` permissions do not grant access to all document types. Use SAF or All files access if eligible.\n\n### App rejected on Google Play because of `MANAGE_EXTERNAL_STORAGE`\n\nRemove the permission unless your app qualifies under Play policy. Use SAF, MediaStore, or Android Photo Picker.\n\n### Selected file path cannot be read\n\nCheck:\n\n```java\nFile file = new File(path);\nboolean exists = file.exists();\nboolean readable = file.canRead();\n```\n\nOn Android 11+, direct file paths may still be restricted without correct access.\n\n### Permission dialog does not appear\n\nPossible causes:\n\n- Permission already denied with \"Don't ask again\".\n- Permission is not valid for current Android version.\n- Permission missing from manifest.\n- You are requesting `READ_EXTERNAL_STORAGE` on Android 13+, where media permissions are required for media.\n- You are trying to request `MANAGE_EXTERNAL_STORAGE` as a runtime permission. It must be granted from Settings.\n\n### `WRITE_EXTERNAL_STORAGE` not working\n\n`WRITE_EXTERNAL_STORAGE` is no longer useful for modern Android shared storage. It should be limited to older versions:\n\n```xml\n\u003cuses-permission\n    android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n    android:maxSdkVersion=\"28\" /\u003e\n```\n\n### `requestLegacyExternalStorage` not working\n\n`android:requestLegacyExternalStorage=\"true\"` helps only for Android 10 legacy behavior. It does not bypass scoped storage on Android 11+ for apps targeting modern SDKs.\n\n---\n\n## Migration guide from v9.x to v10.1.3\n\n### 1. Update dependency\n\n```gradle\nimplementation \"io.github.tutorialsandroid:filepicker:10.1.2\"\n```\n\n### 2. Replace old permission code\n\nOld code often used:\n\n```java\nActivityCompat.requestPermissions(...)\nonRequestPermissionsResult(...)\nstartActivityForResult(...)\n```\n\nRecommended v10 code:\n\n```java\nregisterForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), ...)\nregisterForActivityResult(new ActivityResultContracts.StartActivityForResult(), ...)\n```\n\n### 3. Review manifest\n\nChange old write permission:\n\n```xml\n\u003cuses-permission\n    android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n    android:maxSdkVersion=\"28\" /\u003e\n```\n\nDo not use:\n\n```xml\n\u003cuses-permission\n    android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n    android:maxSdkVersion=\"32\" /\u003e\n```\n\n### 4. Decide your storage strategy\n\nAsk yourself:\n\n- Is my app a file manager or document manager?\n- Is broad storage access the core functionality?\n- Can I use SAF instead?\n- Can I use MediaStore instead?\n- Can I use Android Photo Picker instead?\n\nOnly use `MANAGE_EXTERNAL_STORAGE` when the answer is policy-safe.\n\n### 5. Update dialog size usage if needed\n\nIf your old app looked too narrow on landscape, tablet, foldable, or ultra-wide screens, use the v10.1.3 sizing API:\n\n```java\ndialog.setDialogSizeByPercent(0.85f, 0.75f);\n```\n\nor:\n\n```java\ndialog.setDialogSize(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        ViewGroup.LayoutParams.WRAP_CONTENT\n);\n```\n\n### 6. Test on real devices\n\nTest at least:\n\n- Android 8 or 9\n- Android 10\n- Android 11 or 12\n- Android 13\n- Android 14 or newer\n\nCheck:\n\n- Permission grant\n- Permission deny\n- \"Don't ask again\"\n- Empty folders\n- Hidden files\n- Multiple selection\n- Directory selection\n- Extension filtering\n- Back navigation\n- Screen rotation\n- Large folders\n\n---\n\n## Security and privacy recommendations\n\n- Access files only after a clear user action.\n- Do not scan the whole device silently.\n- Do not upload selected files without explicit consent.\n- Do not collect file names, paths, or metadata unless required.\n- Do not log sensitive file paths in production.\n- Handle unreadable files gracefully.\n- Provide a clear privacy policy.\n- Explain permissions before redirecting users to Settings.\n- Request the minimum permission needed.\n- Prefer SAF/Photo Picker/MediaStore where possible.\n\n---\n\n## FAQ\n\n### Does this library support Android 13?\n\nYes. Android 13 media permissions are documented and supported. For non-media files, Android 13 still follows scoped storage restrictions.\n\n### Does this library support Android 14?\n\nYes, but Android 14 partial media access means users may grant only selected photos/videos. Do not assume full media library access.\n\n### Can I select PDF files on Android 13?\n\nYes, but not with only `READ_MEDIA_*` permissions. For direct path browsing of PDFs on Android 11+, your app needs eligible All files access or should use SAF.\n\n### Can I publish an app with this library on Google Play?\n\nYes, but your permission usage must follow Google Play policy. The library itself is not the problem; the permissions your app declares and how you use them are what matter.\n\n### Will Google Play approve `MANAGE_EXTERNAL_STORAGE`?\n\nOnly if your app has a policy-approved core use case and you complete the required declaration. If your app can use SAF, MediaStore, or Photo Picker instead, approval is unlikely.\n\n### Why not use only Android Photo Picker?\n\nAndroid Photo Picker is excellent for images and videos, but this library is for file/folder picking and direct path workflows.\n\n### Why are some Android/data folders not visible?\n\nModern Android restricts access to many app-private and protected directories. Even All files access does not mean every protected location is freely accessible.\n\n### Is root access required?\n\nNo.\n\n### Can I change dialog width and height?\n\nYes. From v10.1.2, use:\n\n```java\ndialog.setDialogSizeByPercent(0.85f, 0.75f);\n```\n\nor:\n\n```java\ndialog.setDialogSize(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        ViewGroup.LayoutParams.WRAP_CONTENT\n);\n```\n\n### Does this library upload files?\n\nNo. This library only provides local file/folder selection UI. Your app decides what to do with selected paths.\n\n---\n\n## Contributing\n\nContributions are welcome.\n\nHelpful contribution areas:\n\n- Storage Access Framework support\n- URI-based picker mode\n- Compose sample\n- Kotlin sample\n- Better Material UI\n- Android 14/15 behavior testing\n- Accessibility improvements\n- Large-folder performance improvements\n- Unit tests and instrumentation tests\n\nBefore opening a pull request:\n\n1. Test on multiple Android versions.\n2. Keep backward compatibility where possible.\n3. Do not add unnecessary permissions.\n4. Update README when behavior changes.\n5. Explain Play Store impact if storage permissions change.\n\n---\n\n## License\n\n```text\nCopyright (C) 2019 FilePicker\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftutorialsandroid%2Ffilepicker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftutorialsandroid%2Ffilepicker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftutorialsandroid%2Ffilepicker/lists"}