diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f010a280e18..c5fa2a0e95d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -159,6 +159,17 @@ updates:
- dependency-name: "*"
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
+ - package-ecosystem: "gradle"
+ directory: "/packages/file_selector/file_selector/example/android/app"
+ commit-message:
+ prefix: "[file_selector]"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-minor", "version-update:semver-patch"]
+
- package-ecosystem: "gradle"
directory: "/packages/file_selector/file_selector_android/android"
commit-message:
diff --git a/CODEOWNERS b/CODEOWNERS
index dfc8a29c749..21ef69fc030 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -53,6 +53,7 @@ packages/**/*_web/** @ditman
packages/camera/camera_android/** @camsim99
packages/camera/camera_android_camerax/** @camsim99
packages/espresso/** @reidbaker
+packages/file_selector/file_selector_android/** @gmackall
packages/flutter_plugin_android_lifecycle/** @reidbaker
packages/google_maps_flutter/google_maps_flutter_android/** @reidbaker
packages/google_sign_in/google_sign_in_android/** @camsim99
diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md
index 5215787e070..089f2d70b7e 100644
--- a/packages/file_selector/file_selector/CHANGELOG.md
+++ b/packages/file_selector/file_selector/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.5
+
+* Adds an endorsed Android implementation.
+
## 0.9.4
* Adds `getSaveLocation` and deprecates `getSavePath`.
diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md
index dac9746891b..12efb64de74 100644
--- a/packages/file_selector/file_selector/README.md
+++ b/packages/file_selector/file_selector/README.md
@@ -6,9 +6,9 @@
A Flutter plugin that manages files and interactions with file dialogs.
-| | iOS | Linux | macOS | Web | Windows |
-|-------------|---------|-------|--------|-----|-------------|
-| **Support** | iOS 11+ | Any | 10.14+ | Any | Windows 10+ |
+| | Android | iOS | Linux | macOS | Web | Windows |
+|-------------|---------|---------|-------|--------|-----|-------------|
+| **Support** | SDK 19+ | iOS 11+ | Any | 10.14+ | Any | Windows 10+ |
## Usage
@@ -100,23 +100,25 @@ Different platforms support different type group filter options. To avoid
filters that cover all platforms you are targeting, or that you conditionally
pass different `XTypeGroup`s based on `Platform`.
-| | iOS | Linux | macOS | Web | Windows |
-|--------------------------|-----|-------|--------|-----|-------------|
-| `extensions` | | ✔️ | ✔️ | ✔️ | ✔️ |
-| `mimeTypes` | | ✔️ | ✔️† | ✔️ | |
-| `uniformTypeIdentifiers` | ✔️ | | ✔️ | | |
-| `webWildCards` | | | | ✔️ | |
+| | Andoid | iOS | Linux | macOS | Web | Windows |
+|--------------------------|--------|-----|-------|--------|-----|-------------|
+| `extensions` | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ |
+| `mimeTypes` | ✔️ | | ✔️ | ✔️† | ✔️ | |
+| `uniformTypeIdentifiers` | | ✔️ | | ✔️ | | |
+| `webWildCards` | | | | | ✔️ | |
† `mimeTypes` are not supported on version of macOS earlier than 11 (Big Sur).
### Features supported by platform
-| Feature | Description | iOS | Linux | macOS | Windows | Web |
-| ---------------------- |----------------------------------- |--------- | ---------- | -------- | ------------ | ----------- |
-| Choose a single file | Pick a file/image | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
-| Choose multiple files | Pick multiple files/images | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
-| Choose a save location | Pick a directory to save a file in | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
-| Choose a directory | Pick a folder and get its path | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
+| Feature | Description | Android | iOS | Linux | macOS | Windows | Web |
+| ---------------------- |----------------------------------- |---------|--------- | ---------- | -------- | ------------ | ----------- |
+| Choose a single file | Pick a file/image | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
+| Choose multiple files | Pick multiple files/images | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
+| Choose a save location | Pick a directory to save a file in | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
+| Choose a directory | Pick a directory and get its path | ✔️† | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
+
+† Choosing a directory is no supported on versions of Android before SDK 21 (Lollipop).
[example]:./example
[entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox
diff --git a/packages/file_selector/file_selector/example/android/.gitignore b/packages/file_selector/file_selector/example/android/.gitignore
new file mode 100644
index 00000000000..6f568019d3c
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/file_selector/file_selector/example/android/app/build.gradle b/packages/file_selector/file_selector/example/android/app/build.gradle
new file mode 100644
index 00000000000..56af9bc1f95
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/build.gradle
@@ -0,0 +1,71 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ namespace "dev.flutter.plugins.file_selector_example"
+ compileSdkVersion flutter.compileSdkVersion
+ ndkVersion flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ applicationId "dev.flutter.plugins.file_selector_example"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+ minSdkVersion 19
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/file_selector/file_selector/example/android/app/src/debug/AndroidManifest.xml b/packages/file_selector/file_selector/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000000..399f6981d5d
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/AndroidManifest.xml b/packages/file_selector/file_selector/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..2aa121a6d82
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/kotlin/dev/flutter/plugins/file_selector_example/MainActivity.kt b/packages/file_selector/file_selector/example/android/app/src/main/kotlin/dev/flutter/plugins/file_selector_example/MainActivity.kt
new file mode 100644
index 00000000000..9bf2bbdb810
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/kotlin/dev/flutter/plugins/file_selector_example/MainActivity.kt
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package dev.flutter.plugins.file_selector_example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/file_selector/file_selector/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000000..f74085f3f6a
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/drawable/launch_background.xml b/packages/file_selector/file_selector/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000000..304732f8842
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..db77bb4b7b0
Binary files /dev/null and b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..17987b79bb8
Binary files /dev/null and b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..09d4391482b
Binary files /dev/null and b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..d5f1c8d34e7
Binary files /dev/null and b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..4d6372eebdb
Binary files /dev/null and b/packages/file_selector/file_selector/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/values-night/styles.xml b/packages/file_selector/file_selector/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000000..06952be745f
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/main/res/values/styles.xml b/packages/file_selector/file_selector/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000000..cb1ef88056e
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/app/src/profile/AndroidManifest.xml b/packages/file_selector/file_selector/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000000..399f6981d5d
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/packages/file_selector/file_selector/example/android/build.gradle b/packages/file_selector/file_selector/example/android/build.gradle
new file mode 100644
index 00000000000..f7eb7f63ce1
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.7.10'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.3.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/file_selector/file_selector/example/android/gradle.properties b/packages/file_selector/file_selector/example/android/gradle.properties
new file mode 100644
index 00000000000..94adc3a3f97
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/file_selector/file_selector/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/file_selector/file_selector/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..3c472b99c6f
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
diff --git a/packages/file_selector/file_selector/example/android/settings.gradle b/packages/file_selector/file_selector/example/android/settings.gradle
new file mode 100644
index 00000000000..44e62bcf06a
--- /dev/null
+++ b/packages/file_selector/file_selector/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart
index 9236a330d04..3619cb0dac5 100644
--- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart
+++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart
@@ -50,7 +50,7 @@ class GetDirectoryPage extends StatelessWidget {
),
onPressed: _isIOS ? null : () => _getDirectoryPath(context),
child: const Text(
- 'Press to ask user to choose a directory (not supported on iOS).',
+ 'Press to ask user to choose a directory.',
),
),
],
diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart
index 052dba04333..25db9eaba40 100644
--- a/packages/file_selector/file_selector/example/lib/home_page.dart
+++ b/packages/file_selector/file_selector/example/lib/home_page.dart
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:io' show Platform;
+
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// Home Page of the application
@@ -43,25 +46,33 @@ class HomePage extends StatelessWidget {
child: const Text('Open multiple images'),
onPressed: () => Navigator.pushNamed(context, '/open/images'),
),
- const SizedBox(height: 10),
- ElevatedButton(
- style: style,
- child: const Text('Save a file'),
- onPressed: () => Navigator.pushNamed(context, '/save/text'),
- ),
- const SizedBox(height: 10),
- ElevatedButton(
- style: style,
- child: const Text('Open a get directory dialog'),
- onPressed: () => Navigator.pushNamed(context, '/directory'),
- ),
- const SizedBox(height: 10),
- ElevatedButton(
- style: style,
- child: const Text('Open a get multi directories dialog'),
- onPressed: () =>
- Navigator.pushNamed(context, '/multi-directories'),
- ),
+ // TODO(stuartmorgan): Replace these checks with support queries once
+ // https://github.com/flutter/flutter/issues/127328 is implemented.
+ if (kIsWeb || !(Platform.isAndroid || Platform.isIOS)) ...[
+ const SizedBox(height: 10),
+ ElevatedButton(
+ style: style,
+ child: const Text('Save a file'),
+ onPressed: () => Navigator.pushNamed(context, '/save/text'),
+ ),
+ ],
+ if (!(kIsWeb || Platform.isIOS)) ...[
+ const SizedBox(height: 10),
+ ElevatedButton(
+ style: style,
+ child: const Text('Open a get directory dialog'),
+ onPressed: () => Navigator.pushNamed(context, '/directory'),
+ ),
+ ],
+ if (!(kIsWeb || Platform.isAndroid || Platform.isIOS)) ...[
+ const SizedBox(height: 10),
+ ElevatedButton(
+ style: style,
+ child: const Text('Open a get multi directories dialog'),
+ onPressed: () =>
+ Navigator.pushNamed(context, '/multi-directories'),
+ ),
+ ],
],
),
),
diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart
index 5121e9d55a4..5223e6ab600 100644
--- a/packages/file_selector/file_selector/example/lib/open_text_page.dart
+++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:file_selector/file_selector.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
@@ -19,8 +20,8 @@ class OpenTextPage extends StatelessWidget {
// This demonstrates using an initial directory for the prompt, which should
// only be done in cases where the application can likely predict where the
// file would be. In most cases, this parameter should not be provided.
- final String initialDirectory =
- (await getApplicationDocumentsDirectory()).path;
+ final String? initialDirectory =
+ kIsWeb ? null : (await getApplicationDocumentsDirectory()).path;
final XFile? file = await openFile(
acceptedTypeGroups: [typeGroup],
initialDirectory: initialDirectory,
diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart
index e782530914e..5099e3252bb 100644
--- a/packages/file_selector/file_selector/example/lib/save_text_page.dart
+++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart
@@ -85,7 +85,7 @@ class SaveTextPage extends StatelessWidget {
),
onPressed: _isIOS ? null : () => _saveFile(),
child: const Text(
- 'Press to save a text file (not supported on iOS).',
+ 'Press to save a text file.',
),
),
],
diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml
index 6b32deecc33..36cb7a69816 100644
--- a/packages/file_selector/file_selector/pubspec.yaml
+++ b/packages/file_selector/file_selector/pubspec.yaml
@@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting
directories, using native file selection UI.
repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
-version: 0.9.4
+version: 0.9.5
environment:
sdk: ">=2.18.0 <4.0.0"
@@ -12,6 +12,8 @@ environment:
flutter:
plugin:
platforms:
+ android:
+ default_package: file_selector_android
ios:
default_package: file_selector_ios
linux:
@@ -24,6 +26,7 @@ flutter:
default_package: file_selector_windows
dependencies:
+ file_selector_android: ^0.5.0
file_selector_ios: ^0.5.0
file_selector_linux: ^0.9.2
file_selector_macos: ^0.9.3
diff --git a/script/configs/exclude_integration_android.yaml b/script/configs/exclude_integration_android.yaml
index fe51488c706..64193b5e60c 100644
--- a/script/configs/exclude_integration_android.yaml
+++ b/script/configs/exclude_integration_android.yaml
@@ -1 +1,2 @@
-[]
+# Can't use Flutter integration tests due to native modal UI.
+- file_selector