[path_provider]: Migrated path_provider for android to pigeon. (#5959)
diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md
index 4b15e26..6b1b661 100644
--- a/packages/path_provider/path_provider_android/CHANGELOG.md
+++ b/packages/path_provider/path_provider_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.0.15
+
+* Switches the medium from MethodChannels to Pigeon.
+
## 2.0.14
* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors
diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java
new file mode 100644
index 0000000..1e78916
--- /dev/null
+++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java
@@ -0,0 +1,241 @@
+// 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.
+// Autogenerated from Pigeon (v3.1.5), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+package io.flutter.plugins.pathprovider;
+
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MessageCodec;
+import io.flutter.plugin.common.StandardMessageCodec;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Generated class from Pigeon. */
+@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
+public class Messages {
+
+ public enum StorageDirectory {
+ music(0),
+ podcasts(1),
+ ringtones(2),
+ alarms(3),
+ notifications(4),
+ pictures(5),
+ movies(6),
+ downloads(7),
+ dcim(8),
+ documents(9);
+
+ private int index;
+
+ private StorageDirectory(final int index) {
+ this.index = index;
+ }
+ }
+
+ private static class PathProviderApiCodec extends StandardMessageCodec {
+ public static final PathProviderApiCodec INSTANCE = new PathProviderApiCodec();
+
+ private PathProviderApiCodec() {}
+ }
+
+ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+ public interface PathProviderApi {
+ @Nullable
+ String getTemporaryPath();
+
+ @Nullable
+ String getApplicationSupportPath();
+
+ @Nullable
+ String getApplicationDocumentsPath();
+
+ @Nullable
+ String getExternalStoragePath();
+
+ @NonNull
+ List<String> getExternalCachePaths();
+
+ @NonNull
+ List<String> getExternalStoragePaths(@NonNull StorageDirectory directory);
+
+ /** The codec used by PathProviderApi. */
+ static MessageCodec<Object> getCodec() {
+ return PathProviderApiCodec.INSTANCE;
+ }
+
+ /**
+ * Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`.
+ */
+ static void setup(BinaryMessenger binaryMessenger, PathProviderApi api) {
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getTemporaryPath",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ String output = api.getTemporaryPath();
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ String output = api.getApplicationSupportPath();
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ String output = api.getApplicationDocumentsPath();
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getExternalStoragePath",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ String output = api.getExternalStoragePath();
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getExternalCachePaths",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ List<String> output = api.getExternalCachePaths();
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths",
+ getCodec(),
+ taskQueue);
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ Map<String, Object> wrapped = new HashMap<>();
+ try {
+ ArrayList<Object> args = (ArrayList<Object>) message;
+ StorageDirectory directoryArg =
+ args.get(0) == null ? null : StorageDirectory.values()[(int) args.get(0)];
+ if (directoryArg == null) {
+ throw new NullPointerException("directoryArg unexpectedly null.");
+ }
+ List<String> output = api.getExternalStoragePaths(directoryArg);
+ wrapped.put("result", output);
+ } catch (Error | RuntimeException exception) {
+ wrapped.put("error", wrapError(exception));
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ }
+ }
+
+ private static Map<String, Object> wrapError(Throwable exception) {
+ Map<String, Object> errorMap = new HashMap<>();
+ errorMap.put("message", exception.toString());
+ errorMap.put("code", exception.getClass().getSimpleName());
+ errorMap.put(
+ "details",
+ "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
+ return errorMap;
+ }
+}
diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
index 6dcf959..8a5ff48 100644
--- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
+++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java
@@ -7,164 +7,34 @@
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.SettableFuture;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.BinaryMessenger.TaskQueue;
-import io.flutter.plugin.common.MethodCall;
-import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
-import io.flutter.plugin.common.MethodChannel.Result;
-import io.flutter.plugin.common.StandardMethodCodec;
+import io.flutter.plugins.pathprovider.Messages.PathProviderApi;
import io.flutter.util.PathUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
+import javax.annotation.Nullable;
-public class PathProviderPlugin implements FlutterPlugin, MethodCallHandler {
+public class PathProviderPlugin implements FlutterPlugin, PathProviderApi {
static final String TAG = "PathProviderPlugin";
private Context context;
- private MethodChannel channel;
- private PathProviderImpl impl;
-
- /**
- * An abstraction over how to access the paths in a thread-safe manner.
- *
- * <p>We need this so on versions of Flutter that support Background Platform Channels this plugin
- * can take advantage of it.
- *
- * <p>This can be removed after https://github.com/flutter/engine/pull/29147 becomes available on
- * the stable branch.
- */
- private interface PathProviderImpl {
- void getTemporaryDirectory(@NonNull Result result);
-
- void getApplicationDocumentsDirectory(@NonNull Result result);
-
- void getStorageDirectory(@NonNull Result result);
-
- void getExternalCacheDirectories(@NonNull Result result);
-
- void getExternalStorageDirectories(@NonNull String directoryName, @NonNull Result result);
-
- void getApplicationSupportDirectory(@NonNull Result result);
- }
-
- /** The implementation for getting system paths that executes from the platform */
- private class PathProviderPlatformThread implements PathProviderImpl {
- private final Executor uiThreadExecutor = new UiThreadExecutor();
- private final Executor executor =
- Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setNameFormat("path-provider-background-%d")
- .setPriority(Thread.NORM_PRIORITY)
- .build());
-
- public void getTemporaryDirectory(@NonNull Result result) {
- executeInBackground(() -> getPathProviderTemporaryDirectory(), result);
- }
-
- public void getApplicationDocumentsDirectory(@NonNull Result result) {
- executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result);
- }
-
- public void getStorageDirectory(@NonNull Result result) {
- executeInBackground(() -> getPathProviderStorageDirectory(), result);
- }
-
- public void getExternalCacheDirectories(@NonNull Result result) {
- executeInBackground(() -> getPathProviderExternalCacheDirectories(), result);
- }
-
- public void getExternalStorageDirectories(
- @NonNull String directoryName, @NonNull Result result) {
- executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result);
- }
-
- public void getApplicationSupportDirectory(@NonNull Result result) {
- executeInBackground(() -> PathProviderPlugin.this.getApplicationSupportDirectory(), result);
- }
-
- private <T> void executeInBackground(Callable<T> task, Result result) {
- final SettableFuture<T> future = SettableFuture.create();
- Futures.addCallback(
- future,
- new FutureCallback<T>() {
- public void onSuccess(T answer) {
- result.success(answer);
- }
-
- public void onFailure(Throwable t) {
- result.error(t.getClass().getName(), t.getMessage(), null);
- }
- },
- uiThreadExecutor);
- executor.execute(
- () -> {
- try {
- future.set(task.call());
- } catch (Throwable t) {
- future.setException(t);
- }
- });
- }
- }
-
- /** The implementation for getting system paths that executes from a background thread. */
- private class PathProviderBackgroundThread implements PathProviderImpl {
- public void getTemporaryDirectory(@NonNull Result result) {
- result.success(getPathProviderTemporaryDirectory());
- }
-
- public void getApplicationDocumentsDirectory(@NonNull Result result) {
- result.success(getPathProviderApplicationDocumentsDirectory());
- }
-
- public void getStorageDirectory(@NonNull Result result) {
- result.success(getPathProviderStorageDirectory());
- }
-
- public void getExternalCacheDirectories(@NonNull Result result) {
- result.success(getPathProviderExternalCacheDirectories());
- }
-
- public void getExternalStorageDirectories(
- @NonNull String directoryName, @NonNull Result result) {
- result.success(getPathProviderExternalStorageDirectories(directoryName));
- }
-
- public void getApplicationSupportDirectory(@NonNull Result result) {
- result.success(PathProviderPlugin.this.getApplicationSupportDirectory());
- }
- }
public PathProviderPlugin() {}
private void setup(BinaryMessenger messenger, Context context) {
- String channelName = "plugins.flutter.io/path_provider_android";
TaskQueue taskQueue = messenger.makeBackgroundTaskQueue();
try {
- channel =
- (MethodChannel)
- new MethodChannel(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue);
- impl = new PathProviderBackgroundThread();
+ PathProviderApi.setup(messenger, this);
} catch (Exception ex) {
Log.e(TAG, "Received exception while setting up PathProviderPlugin", ex);
}
this.context = context;
- channel.setMethodCallHandler(this);
}
@SuppressWarnings("deprecation")
@@ -180,36 +50,38 @@
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
- channel.setMethodCallHandler(null);
- channel = null;
+ PathProviderApi.setup(binding.getBinaryMessenger(), null);
}
@Override
- public void onMethodCall(MethodCall call, @NonNull Result result) {
- switch (call.method) {
- case "getTemporaryDirectory":
- impl.getTemporaryDirectory(result);
- break;
- case "getApplicationDocumentsDirectory":
- impl.getApplicationDocumentsDirectory(result);
- break;
- case "getStorageDirectory":
- impl.getStorageDirectory(result);
- break;
- case "getExternalCacheDirectories":
- impl.getExternalCacheDirectories(result);
- break;
- case "getExternalStorageDirectories":
- final Integer type = call.argument("type");
- final String directoryName = StorageDirectoryMapper.androidType(type);
- impl.getExternalStorageDirectories(directoryName, result);
- break;
- case "getApplicationSupportDirectory":
- impl.getApplicationSupportDirectory(result);
- break;
- default:
- result.notImplemented();
- }
+ public @Nullable String getTemporaryPath() {
+ return getPathProviderTemporaryDirectory();
+ }
+
+ @Override
+ public @Nullable String getApplicationSupportPath() {
+ return getApplicationSupportDirectory();
+ }
+
+ @Override
+ public @Nullable String getApplicationDocumentsPath() {
+ return getPathProviderApplicationDocumentsDirectory();
+ }
+
+ @Override
+ public @Nullable String getExternalStoragePath() {
+ return getPathProviderStorageDirectory();
+ }
+
+ @Override
+ public @NonNull List<String> getExternalCachePaths() {
+ return getPathProviderExternalCacheDirectories();
+ }
+
+ @Override
+ public @NonNull List<String> getExternalStoragePaths(
+ @NonNull Messages.StorageDirectory directory) {
+ return getPathProviderExternalStorageDirectories(directory);
}
private String getPathProviderTemporaryDirectory() {
@@ -251,17 +123,45 @@
return paths;
}
- private List<String> getPathProviderExternalStorageDirectories(String type) {
+ private String getStorageDirectoryString(@NonNull Messages.StorageDirectory directory) {
+ switch (directory) {
+ case music:
+ return "music";
+ case podcasts:
+ return "podcasts";
+ case ringtones:
+ return "ringtones";
+ case alarms:
+ return "alarms";
+ case notifications:
+ return "notifications";
+ case pictures:
+ return "pictures";
+ case movies:
+ return "movies";
+ case downloads:
+ return "downloads";
+ case dcim:
+ return "dcim";
+ case documents:
+ return "documents";
+ default:
+ throw new RuntimeException("Unrecognized directory: " + directory);
+ }
+ }
+
+ private List<String> getPathProviderExternalStorageDirectories(
+ @NonNull Messages.StorageDirectory directory) {
final List<String> paths = new ArrayList<String>();
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
- for (File dir : context.getExternalFilesDirs(type)) {
+ for (File dir : context.getExternalFilesDirs(getStorageDirectoryString(directory))) {
if (dir != null) {
paths.add(dir.getAbsolutePath());
}
}
} else {
- File dir = context.getExternalFilesDir(type);
+ File dir = context.getExternalFilesDir(getStorageDirectoryString(directory));
if (dir != null) {
paths.add(dir.getAbsolutePath());
}
@@ -269,13 +169,4 @@
return paths;
}
-
- private static class UiThreadExecutor implements Executor {
- private final Handler handler = new Handler(Looper.getMainLooper());
-
- @Override
- public void execute(Runnable command) {
- handler.post(command);
- }
- }
}
diff --git a/packages/path_provider/path_provider_android/lib/messages.g.dart b/packages/path_provider/path_provider_android/lib/messages.g.dart
new file mode 100644
index 0000000..50009f4
--- /dev/null
+++ b/packages/path_provider/path_provider_android/lib/messages.g.dart
@@ -0,0 +1,196 @@
+// 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.
+// Autogenerated from Pigeon (v3.1.5), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
+// @dart = 2.12
+import 'dart:async';
+import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
+
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
+import 'package:flutter/services.dart';
+
+enum StorageDirectory {
+ music,
+ podcasts,
+ ringtones,
+ alarms,
+ notifications,
+ pictures,
+ movies,
+ downloads,
+ dcim,
+ documents,
+}
+
+class _PathProviderApiCodec extends StandardMessageCodec {
+ const _PathProviderApiCodec();
+}
+
+class PathProviderApi {
+ /// Constructor for [PathProviderApi]. The [binaryMessenger] named argument is
+ /// available for dependency injection. If it is left null, the default
+ /// BinaryMessenger will be used which routes to the host platform.
+ PathProviderApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec<Object?> codec = _PathProviderApiCodec();
+
+ Future<String?> getTemporaryPath() async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(null) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as String?);
+ }
+ }
+
+ Future<String?> getApplicationSupportPath() async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(null) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as String?);
+ }
+ }
+
+ Future<String?> getApplicationDocumentsPath() async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(null) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as String?);
+ }
+ }
+
+ Future<String?> getExternalStoragePath() async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePath', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(null) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as String?);
+ }
+ }
+
+ Future<List<String?>> getExternalCachePaths() async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalCachePaths', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(null) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else if (replyMap['result'] == null) {
+ throw PlatformException(
+ code: 'null-error',
+ message: 'Host platform returned null value for non-null return value.',
+ );
+ } else {
+ return (replyMap['result'] as List<Object?>?)!.cast<String?>();
+ }
+ }
+
+ Future<List<String?>> getExternalStoragePaths(
+ StorageDirectory arg_directory) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap = await channel
+ .send(<Object?>[arg_directory.index]) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else if (replyMap['result'] == null) {
+ throw PlatformException(
+ code: 'null-error',
+ message: 'Host platform returned null value for non-null return value.',
+ );
+ } else {
+ return (replyMap['result'] as List<Object?>?)!.cast<String?>();
+ }
+ }
+}
diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart
index b0f3808..4f08d7a 100644
--- a/packages/path_provider/path_provider_android/lib/path_provider_android.dart
+++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart
@@ -2,16 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+import 'messages.g.dart' as messages;
+
+messages.StorageDirectory _convertStorageDirectory(StorageDirectory directory) {
+ switch (directory) {
+ case StorageDirectory.music:
+ return messages.StorageDirectory.music;
+ case StorageDirectory.podcasts:
+ return messages.StorageDirectory.podcasts;
+ case StorageDirectory.ringtones:
+ return messages.StorageDirectory.ringtones;
+ case StorageDirectory.alarms:
+ return messages.StorageDirectory.alarms;
+ case StorageDirectory.notifications:
+ return messages.StorageDirectory.notifications;
+ case StorageDirectory.pictures:
+ return messages.StorageDirectory.pictures;
+ case StorageDirectory.movies:
+ return messages.StorageDirectory.movies;
+ case StorageDirectory.downloads:
+ return messages.StorageDirectory.downloads;
+ case StorageDirectory.dcim:
+ return messages.StorageDirectory.dcim;
+ case StorageDirectory.documents:
+ return messages.StorageDirectory.documents;
+ }
+}
/// The Android implementation of [PathProviderPlatform].
class PathProviderAndroid extends PathProviderPlatform {
- /// The method channel used to interact with the native platform.
- @visibleForTesting
- MethodChannel methodChannel =
- const MethodChannel('plugins.flutter.io/path_provider_android');
+ final messages.PathProviderApi _api = messages.PathProviderApi();
/// Registers this class as the default instance of [PathProviderPlatform].
static void registerWith() {
@@ -20,12 +41,12 @@
@override
Future<String?> getTemporaryPath() {
- return methodChannel.invokeMethod<String>('getTemporaryDirectory');
+ return _api.getTemporaryPath();
}
@override
Future<String?> getApplicationSupportPath() {
- return methodChannel.invokeMethod<String>('getApplicationSupportDirectory');
+ return _api.getApplicationSupportPath();
}
@override
@@ -35,29 +56,27 @@
@override
Future<String?> getApplicationDocumentsPath() {
- return methodChannel
- .invokeMethod<String>('getApplicationDocumentsDirectory');
+ return _api.getApplicationDocumentsPath();
}
@override
Future<String?> getExternalStoragePath() {
- return methodChannel.invokeMethod<String>('getStorageDirectory');
+ return _api.getExternalStoragePath();
}
@override
- Future<List<String>?> getExternalCachePaths() {
- return methodChannel
- .invokeListMethod<String>('getExternalCacheDirectories');
+ Future<List<String>?> getExternalCachePaths() async {
+ return (await _api.getExternalCachePaths()).cast<String>();
}
@override
Future<List<String>?> getExternalStoragePaths({
StorageDirectory? type,
}) async {
- return methodChannel.invokeListMethod<String>(
- 'getExternalStorageDirectories',
- <String, dynamic>{'type': type?.index},
- );
+ return type == null
+ ? <String>[]
+ : (await _api.getExternalStoragePaths(_convertStorageDirectory(type)))
+ .cast<String>();
}
@override
diff --git a/packages/path_provider/path_provider_android/pigeons/copyright.txt b/packages/path_provider/path_provider_android/pigeons/copyright.txt
new file mode 100644
index 0000000..1236b63
--- /dev/null
+++ b/packages/path_provider/path_provider_android/pigeons/copyright.txt
@@ -0,0 +1,3 @@
+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.
diff --git a/packages/path_provider/path_provider_android/pigeons/messages.dart b/packages/path_provider/path_provider_android/pigeons/messages.dart
new file mode 100644
index 0000000..4641667
--- /dev/null
+++ b/packages/path_provider/path_provider_android/pigeons/messages.dart
@@ -0,0 +1,43 @@
+// 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.
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(PigeonOptions(
+ input: 'pigeons/messages.dart',
+ javaOut:
+ 'android/src/main/java/io/flutter/plugins/pathprovider/Messages.java',
+ javaOptions: JavaOptions(
+ className: 'Messages', package: 'io.flutter.plugins.pathprovider'),
+ dartOut: 'lib/messages.g.dart',
+ dartTestOut: 'test/messages_test.g.dart',
+ copyrightHeader: 'pigeons/copyright.txt',
+))
+enum StorageDirectory {
+ music,
+ podcasts,
+ ringtones,
+ alarms,
+ notifications,
+ pictures,
+ movies,
+ downloads,
+ dcim,
+ documents,
+}
+
+@HostApi(dartHostTestHandler: 'TestPathProviderApi')
+abstract class PathProviderApi {
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ String? getTemporaryPath();
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ String? getApplicationSupportPath();
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ String? getApplicationDocumentsPath();
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ String? getExternalStoragePath();
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ List<String?> getExternalCachePaths();
+ @TaskQueue(type: TaskQueueType.serialBackgroundThread)
+ List<String?> getExternalStoragePaths(StorageDirectory directory);
+}
diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml
index f1dc92a..64b953b 100644
--- a/packages/path_provider/path_provider_android/pubspec.yaml
+++ b/packages/path_provider/path_provider_android/pubspec.yaml
@@ -2,11 +2,11 @@
description: Android implementation of the path_provider plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
-version: 2.0.14
+version: 2.0.15
environment:
sdk: ">=2.14.0 <3.0.0"
- flutter: ">=2.8.1"
+ flutter: ">=3.0.0"
flutter:
plugin:
@@ -29,4 +29,5 @@
sdk: flutter
integration_test:
sdk: flutter
+ pigeon: ^3.1.5
test: ^1.16.0
diff --git a/packages/path_provider/path_provider_android/test/messages_test.g.dart b/packages/path_provider/path_provider_android/test/messages_test.g.dart
new file mode 100644
index 0000000..dc8ee55
--- /dev/null
+++ b/packages/path_provider/path_provider_android/test/messages_test.g.dart
@@ -0,0 +1,131 @@
+// 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.
+// Autogenerated from Pigeon (v3.1.5), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis
+// ignore_for_file: avoid_relative_lib_imports
+// @dart = 2.12
+import 'dart:async';
+import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+// The following line is edited by hand to avoid confusing dart with overloaded types.
+import 'package:path_provider_android/messages.g.dart';
+
+class _TestPathProviderApiCodec extends StandardMessageCodec {
+ const _TestPathProviderApiCodec();
+}
+
+abstract class TestPathProviderApi {
+ static const MessageCodec<Object?> codec = _TestPathProviderApiCodec();
+
+ String? getTemporaryPath();
+ String? getApplicationSupportPath();
+ String? getApplicationDocumentsPath();
+ String? getExternalStoragePath();
+ List<String?> getExternalCachePaths();
+ List<String?> getExternalStoragePaths(StorageDirectory directory);
+ static void setup(TestPathProviderApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ // ignore message
+ final String? output = api.getTemporaryPath();
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ // ignore message
+ final String? output = api.getApplicationSupportPath();
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath',
+ codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ // ignore message
+ final String? output = api.getApplicationDocumentsPath();
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePath', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ // ignore message
+ final String? output = api.getExternalStoragePath();
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalCachePaths', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ // ignore message
+ final List<String?> output = api.getExternalCachePaths();
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths', codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMockMessageHandler(null);
+ } else {
+ channel.setMockMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+
+ /// TODO(gaaclarke): The following line was tweaked by hand to address
+ /// https://github.com/flutter/flutter/issues/105742. Alternatively
+ /// the tests could be written with a mock BinaryMessenger but this is
+ /// how we want to address it eventually.
+ final StorageDirectory? arg_directory =
+ StorageDirectory.values[args[0] as int];
+ assert(arg_directory != null,
+ 'Argument for dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths was null, expected non-null StorageDirectory.');
+ final List<String?> output =
+ api.getExternalStoragePaths(arg_directory!);
+ return <Object?, Object?>{'result': output};
+ });
+ }
+ }
+ }
+}
diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart
index d2f9682..2fe4a51 100644
--- a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart
+++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart
@@ -2,73 +2,59 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:path_provider_android/messages.g.dart' as messages;
import 'package:path_provider_android/path_provider_android.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
+import 'messages_test.g.dart';
+
+const String kTemporaryPath = 'temporaryPath';
+const String kApplicationSupportPath = 'applicationSupportPath';
+const String kLibraryPath = 'libraryPath';
+const String kApplicationDocumentsPath = 'applicationDocumentsPath';
+const String kExternalCachePaths = 'externalCachePaths';
+const String kExternalStoragePaths = 'externalStoragePaths';
+const String kDownloadsPath = 'downloadsPath';
+
+class _Api implements TestPathProviderApi {
+ @override
+ String? getApplicationDocumentsPath() => kApplicationDocumentsPath;
+
+ @override
+ String? getApplicationSupportPath() => kApplicationSupportPath;
+
+ @override
+ List<String?> getExternalCachePaths() => <String>[kExternalCachePaths];
+
+ @override
+ String? getExternalStoragePath() => kExternalStoragePaths;
+
+ @override
+ List<String?> getExternalStoragePaths(messages.StorageDirectory directory) =>
+ <String>[kExternalStoragePaths];
+
+ @override
+ String? getTemporaryPath() => kTemporaryPath;
+}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
- const String kTemporaryPath = 'temporaryPath';
- const String kApplicationSupportPath = 'applicationSupportPath';
- const String kLibraryPath = 'libraryPath';
- const String kApplicationDocumentsPath = 'applicationDocumentsPath';
- const String kExternalCachePaths = 'externalCachePaths';
- const String kExternalStoragePaths = 'externalStoragePaths';
- const String kDownloadsPath = 'downloadsPath';
group('PathProviderAndroid', () {
late PathProviderAndroid pathProvider;
- final List<MethodCall> log = <MethodCall>[];
setUp(() async {
pathProvider = PathProviderAndroid();
- TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
- .setMockMethodCallHandler(pathProvider.methodChannel,
- (MethodCall methodCall) async {
- log.add(methodCall);
- switch (methodCall.method) {
- case 'getTemporaryDirectory':
- return kTemporaryPath;
- case 'getApplicationSupportDirectory':
- return kApplicationSupportPath;
- case 'getLibraryDirectory':
- return kLibraryPath;
- case 'getApplicationDocumentsDirectory':
- return kApplicationDocumentsPath;
- case 'getExternalStorageDirectories':
- return <String>[kExternalStoragePaths];
- case 'getExternalCacheDirectories':
- return <String>[kExternalCachePaths];
- case 'getDownloadsDirectory':
- return kDownloadsPath;
- default:
- return null;
- }
- });
- });
-
- tearDown(() {
- log.clear();
+ TestPathProviderApi.setup(_Api());
});
test('getTemporaryPath', () async {
final String? path = await pathProvider.getTemporaryPath();
- expect(
- log,
- <Matcher>[isMethodCall('getTemporaryDirectory', arguments: null)],
- );
expect(path, kTemporaryPath);
});
test('getApplicationSupportPath', () async {
final String? path = await pathProvider.getApplicationSupportPath();
- expect(
- log,
- <Matcher>[
- isMethodCall('getApplicationSupportDirectory', arguments: null)
- ],
- );
expect(path, kApplicationSupportPath);
});
@@ -83,47 +69,32 @@
test('getApplicationDocumentsPath', () async {
final String? path = await pathProvider.getApplicationDocumentsPath();
- expect(
- log,
- <Matcher>[
- isMethodCall('getApplicationDocumentsDirectory', arguments: null)
- ],
- );
expect(path, kApplicationDocumentsPath);
});
test('getExternalCachePaths succeeds', () async {
final List<String>? result = await pathProvider.getExternalCachePaths();
- expect(
- log,
- <Matcher>[isMethodCall('getExternalCacheDirectories', arguments: null)],
- );
expect(result!.length, 1);
expect(result.first, kExternalCachePaths);
});
for (final StorageDirectory? type in <StorageDirectory?>[
- null,
...StorageDirectory.values
]) {
test('getExternalStoragePaths (type: $type) android succeeds', () async {
final List<String>? result =
await pathProvider.getExternalStoragePaths(type: type);
- expect(
- log,
- <Matcher>[
- isMethodCall(
- 'getExternalStorageDirectories',
- arguments: <String, dynamic>{'type': type?.index},
- )
- ],
- );
-
expect(result!.length, 1);
expect(result.first, kExternalStoragePaths);
});
} // end of for-loop
+ test('getExternalStoragePaths with null android succeeds', () async {
+ final List<String>? result =
+ await pathProvider.getExternalStoragePaths(type: null);
+ expect(result!.length, 0);
+ });
+
test('getDownloadsPath fails', () async {
try {
await pathProvider.getDownloadsPath();