Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[path_provider] started supporting background platform channels
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke committed Oct 22, 2021
1 parent c85fa03 commit 8cc0451
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 45 deletions.
5 changes: 5 additions & 0 deletions packages/path_provider/path_provider/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.0.6

* Added support for Background Platform Channels on Android when it is
available.

## 2.0.5

* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,172 @@
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.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.MethodCodec;
import io.flutter.plugin.common.StandardMethodCodec;
import io.flutter.util.PathUtils;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class PathProviderPlugin implements FlutterPlugin, MethodCallHandler {

static final String TAG = "PathProviderPlugin";
private Context context;
private MethodChannel channel;
private final Executor uiThreadExecutor = new UiThreadExecutor();
private final Executor executor =
Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder()
.setNameFormat("path-provider-background-%d")
.setPriority(Thread.NORM_PRIORITY)
.build());
private PathProviderImpl impl;

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);
}

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);
}
});
}
}

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";
// TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147
// becomes available on the stable branch.
try {
Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel");
Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue");
Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue");
Object taskQueue = makeBackgroundTaskQueue.invoke(messenger);
Constructor<MethodChannel> constructor =
methodChannelClass.getConstructor(
BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass);
channel =
constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue);
impl = new PathProviderBackgroundThread();
Log.d(TAG, "use taskqueues");
} catch (Exception ex) {
channel = new MethodChannel(messenger, channelName);
impl = new PathProviderPlatformThread();
Log.d(TAG, "don't use taskqueues");
}
this.context = context;
channel.setMethodCallHandler(this);
}

@SuppressWarnings("deprecation")
public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
PathProviderPlugin instance = new PathProviderPlugin();
instance.channel = new MethodChannel(registrar.messenger(), "plugins.flutter.io/path_provider");
instance.context = registrar.context();
instance.channel.setMethodCallHandler(instance);
instance.setup(registrar.messenger(), registrar.context());
}

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
channel = new MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/path_provider");
context = binding.getApplicationContext();
channel.setMethodCallHandler(this);
setup(binding.getBinaryMessenger(), binding.getApplicationContext());
}

@Override
Expand All @@ -62,52 +183,28 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel = null;
}

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);
}
});
}

@Override
public void onMethodCall(MethodCall call, @NonNull Result result) {
switch (call.method) {
case "getTemporaryDirectory":
executeInBackground(() -> getPathProviderTemporaryDirectory(), result);
impl.getTemporaryDirectory(result);
break;
case "getApplicationDocumentsDirectory":
executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result);
impl.getApplicationDocumentsDirectory(result);
break;
case "getStorageDirectory":
executeInBackground(() -> getPathProviderStorageDirectory(), result);
impl.getStorageDirectory(result);
break;
case "getExternalCacheDirectories":
executeInBackground(() -> getPathProviderExternalCacheDirectories(), result);
impl.getExternalCacheDirectories(result);
break;
case "getExternalStorageDirectories":
final Integer type = call.argument("type");
final String directoryName = StorageDirectoryMapper.androidType(type);
executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result);
impl.getExternalStorageDirectories(directoryName, result);
break;
case "getApplicationSupportDirectory":
executeInBackground(() -> getApplicationSupportDirectory(), result);
impl.getApplicationSupportDirectory(result);
break;
default:
result.notImplemented();
Expand Down
2 changes: 1 addition & 1 deletion packages/path_provider/path_provider/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: path_provider
description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.
repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
version: 2.0.5
version: 2.0.6

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down

0 comments on commit 8cc0451

Please sign in to comment.