diff --git a/packages/path_provider/CHANGELOG.md b/packages/path_provider/CHANGELOG.md index 537480b23d8c..bcb394c51f06 100644 --- a/packages/path_provider/CHANGELOG.md +++ b/packages/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.2 + +* Support Android devices with multiple external storage options. + ## 0.4.1 * Updated Gradle tooling to match Android Studio 3.1.2. diff --git a/packages/path_provider/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index d56cb2b60ca1..d566760d848d 100644 --- a/packages/path_provider/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -4,6 +4,8 @@ package io.flutter.plugins.pathprovider; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Environment; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -11,8 +13,12 @@ import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.Registrar; import io.flutter.util.PathUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.List; public class PathProviderPlugin implements MethodCallHandler { + private final Registrar mRegistrar; public static void registerWith(Registrar registrar) { @@ -38,6 +44,13 @@ public void onMethodCall(MethodCall call, Result result) { case "getStorageDirectory": result.success(getPathProviderStorageDirectory()); break; + case "getExternalCacheDirectories": + result.success(getPathProviderExternalCacheDirectories()); + break; + case "getExternalStorageDirectories": + final String type = call.argument("type"); + result.success(getPathProviderExternalStorageDirectories(type)); + break; default: result.notImplemented(); } @@ -54,4 +67,42 @@ private String getPathProviderApplicationDocumentsDirectory() { private String getPathProviderStorageDirectory() { return Environment.getExternalStorageDirectory().getAbsolutePath(); } + + private List getPathProviderExternalCacheDirectories() { + final List paths = new ArrayList<>(); + + if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { + for (File dir : mRegistrar.context().getExternalCacheDirs()) { + if (dir != null) { + paths.add(dir.getAbsolutePath()); + } + } + } else { + File dir = mRegistrar.context().getExternalCacheDir(); + if (dir != null) { + paths.add(dir.getAbsolutePath()); + } + } + + return paths; + } + + private List getPathProviderExternalStorageDirectories(String type) { + final List paths = new ArrayList<>(); + + if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { + for (File dir : mRegistrar.context().getExternalFilesDirs(type)) { + if (dir != null) { + paths.add(dir.getAbsolutePath()); + } + } + } else { + File dir = mRegistrar.context().getExternalFilesDir(type); + if (dir != null) { + paths.add(dir.getAbsolutePath()); + } + } + + return paths; + } } diff --git a/packages/path_provider/example/lib/main.dart b/packages/path_provider/example/lib/main.dart index 9311896128ba..96c62bd0bcde 100644 --- a/packages/path_provider/example/lib/main.dart +++ b/packages/path_provider/example/lib/main.dart @@ -37,6 +37,8 @@ class _MyHomePageState extends State { Future _tempDirectory; Future _appDocumentsDirectory; Future _externalDocumentsDirectory; + Future> _externalStorageDirectories; + Future> _externalCacheDirectories; void _requestTempDirectory() { setState(() { @@ -59,6 +61,23 @@ class _MyHomePageState extends State { return Padding(padding: const EdgeInsets.all(16.0), child: text); } + Widget _buildDirectories( + BuildContext context, AsyncSnapshot> snapshot) { + Text text = const Text(''); + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + text = Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + final String combined = + snapshot.data.map((Directory d) => d.path).join(', '); + text = Text('paths: $combined'); + } else { + text = const Text('path unavailable'); + } + } + return Padding(padding: const EdgeInsets.all(16.0), child: text); + } + void _requestAppDocumentsDirectory() { setState(() { _appDocumentsDirectory = getApplicationDocumentsDirectory(); @@ -71,13 +90,25 @@ class _MyHomePageState extends State { }); } + void _requestExternalStorageDirectories(String type) { + setState(() { + _externalStorageDirectories = getExternalStorageDirectories(type); + }); + } + + void _requestExternalCacheDirectories() { + setState(() { + _externalCacheDirectories = getExternalCacheDirectories(); + }); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), - body: Center( + body: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -92,10 +123,8 @@ class _MyHomePageState extends State { ), ], ), - Expanded( - child: FutureBuilder( - future: _tempDirectory, builder: _buildDirectory), - ), + FutureBuilder( + future: _tempDirectory, builder: _buildDirectory), Column( children: [ Padding( @@ -107,10 +136,8 @@ class _MyHomePageState extends State { ), ], ), - Expanded( - child: FutureBuilder( - future: _appDocumentsDirectory, builder: _buildDirectory), - ), + FutureBuilder( + future: _appDocumentsDirectory, builder: _buildDirectory), Column(children: [ Padding( padding: const EdgeInsets.all(16.0), @@ -122,11 +149,36 @@ class _MyHomePageState extends State { ), ), ]), - Expanded( - child: FutureBuilder( - future: _externalDocumentsDirectory, - builder: _buildDirectory), - ), + FutureBuilder( + future: _externalDocumentsDirectory, builder: _buildDirectory), + Column(children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: RaisedButton( + child: Text( + '${Platform.isIOS ? "External directories are unavailable " "on iOS" : "Get External Storage Directories"}'), + onPressed: Platform.isIOS + ? null + : () => _requestExternalStorageDirectories(null), + ), + ), + ]), + FutureBuilder>( + future: _externalStorageDirectories, + builder: _buildDirectories), + Column(children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: RaisedButton( + child: Text( + '${Platform.isIOS ? "External directories are unavailable " "on iOS" : "Get External Cache Directories"}'), + onPressed: + Platform.isIOS ? null : _requestExternalCacheDirectories, + ), + ), + ]), + FutureBuilder>( + future: _externalCacheDirectories, builder: _buildDirectories), ], ), ), diff --git a/packages/path_provider/lib/path_provider.dart b/packages/path_provider/lib/path_provider.dart index 905685b86586..ffbae276f384 100644 --- a/packages/path_provider/lib/path_provider.dart +++ b/packages/path_provider/lib/path_provider.dart @@ -61,3 +61,51 @@ Future getExternalStorageDirectory() async { } return Directory(path); } + +/// Paths to directories where application specific cache data can be stored. +/// These paths typically reside on external storage like separate partitions +/// or SD cards. Phones may have multiple storage directories available. +/// +/// The current operating system should be determined before issuing this +/// function call, as this functionality is only available on Android. +/// +/// On iOS, this function throws an UnsupportedError as it is not possible +/// to access outside the app's sandbox. +/// +/// On Android this returns Context.getExternalCacheDirs() or +/// Context.getExternalCacheDir() on API levels below 19. +Future> getExternalCacheDirectories() async { + if (Platform.isIOS) + throw UnsupportedError("Functionality not available on iOS"); + final List paths = + await _channel.invokeMethod('getExternalCacheDirectories'); + + return paths.map((dynamic path) => Directory(path)).toList(); +} + +/// Paths to directories where application specific data can be stored. +/// These paths typically reside on external storage like separate partitions +/// or SD cards. Phones may have multiple storage directories available. +/// +/// The current operating system should be determined before issuing this +/// function call, as this functionality is only available on Android. +/// +/// On iOS, this function throws an UnsupportedError as it is not possible +/// to access outside the app's sandbox. +/// +/// The parameter type is optional. If it is set, it must be one of the +/// "DIRECTORY" constants defined in `android.os.Environment`, e.g. +/// https://developer.android.com/reference/android/os/Environment#DIRECTORY_MUSIC +/// +/// On Android this returns Context.getExternalFilesDirs(String type) or +/// Context.getExternalFilesDir(String type) on API levels below 19. +Future> getExternalStorageDirectories(String type) async { + if (Platform.isIOS) + throw UnsupportedError("Functionality not available on iOS"); + final List paths = await _channel.invokeMethod( + 'getExternalStorageDirectories', + {"type": type}, + ); + + return paths.map((dynamic path) => Directory(path)).toList(); +} diff --git a/packages/path_provider/pubspec.yaml b/packages/path_provider/pubspec.yaml index 564bbf81a74a..5e10423a5bc3 100644 --- a/packages/path_provider/pubspec.yaml +++ b/packages/path_provider/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for getting commonly used locations on the Android & iOS file systems, such as the temp and app data directories. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider -version: 0.4.1 +version: 0.4.2 flutter: plugin: