99import android .os .Build .VERSION_CODES ;
1010import android .os .Handler ;
1111import android .os .Looper ;
12+ import android .util .Log ;
1213import androidx .annotation .NonNull ;
1314import com .google .common .util .concurrent .FutureCallback ;
1415import com .google .common .util .concurrent .Futures ;
1516import com .google .common .util .concurrent .SettableFuture ;
1617import com .google .common .util .concurrent .ThreadFactoryBuilder ;
1718import io .flutter .embedding .engine .plugins .FlutterPlugin ;
19+ import io .flutter .plugin .common .BinaryMessenger ;
1820import io .flutter .plugin .common .MethodCall ;
1921import io .flutter .plugin .common .MethodChannel ;
2022import io .flutter .plugin .common .MethodChannel .MethodCallHandler ;
2123import io .flutter .plugin .common .MethodChannel .Result ;
24+ import io .flutter .plugin .common .MethodCodec ;
25+ import io .flutter .plugin .common .StandardMethodCodec ;
2226import io .flutter .util .PathUtils ;
2327import java .io .File ;
28+ import java .lang .reflect .Constructor ;
29+ import java .lang .reflect .Method ;
2430import java .util .ArrayList ;
2531import java .util .List ;
2632import java .util .concurrent .Callable ;
2733import java .util .concurrent .Executor ;
2834import java .util .concurrent .Executors ;
2935
3036public class PathProviderPlugin implements FlutterPlugin , MethodCallHandler {
31-
37+ static final String TAG = "PathProviderPlugin" ;
3238 private Context context ;
3339 private MethodChannel channel ;
34- private final Executor uiThreadExecutor = new UiThreadExecutor ();
35- private final Executor executor =
36- Executors .newSingleThreadExecutor (
37- new ThreadFactoryBuilder ()
38- .setNameFormat ("path-provider-background-%d" )
39- .setPriority (Thread .NORM_PRIORITY )
40- .build ());
40+ private PathProviderImpl impl ;
41+
42+ /**
43+ * An abstraction over how to access the paths in a thread-safe manner.
44+ *
45+ * <p>We need this so on versions of Flutter that support Background Platform Channels this plugin
46+ * can take advantage of it.
47+ *
48+ * <p>This can be removed after https://github.com/flutter/engine/pull/29147 becomes available on
49+ * the stable branch.
50+ */
51+ private interface PathProviderImpl {
52+ void getTemporaryDirectory (@ NonNull Result result );
53+
54+ void getApplicationDocumentsDirectory (@ NonNull Result result );
55+
56+ void getStorageDirectory (@ NonNull Result result );
57+
58+ void getExternalCacheDirectories (@ NonNull Result result );
59+
60+ void getExternalStorageDirectories (@ NonNull String directoryName , @ NonNull Result result );
61+
62+ void getApplicationSupportDirectory (@ NonNull Result result );
63+ }
64+
65+ /** The implementation for getting system paths that executes from the platform */
66+ private class PathProviderPlatformThread implements PathProviderImpl {
67+ private final Executor uiThreadExecutor = new UiThreadExecutor ();
68+ private final Executor executor =
69+ Executors .newSingleThreadExecutor (
70+ new ThreadFactoryBuilder ()
71+ .setNameFormat ("path-provider-background-%d" )
72+ .setPriority (Thread .NORM_PRIORITY )
73+ .build ());
74+
75+ public void getTemporaryDirectory (@ NonNull Result result ) {
76+ executeInBackground (() -> getPathProviderTemporaryDirectory (), result );
77+ }
78+
79+ public void getApplicationDocumentsDirectory (@ NonNull Result result ) {
80+ executeInBackground (() -> getPathProviderApplicationDocumentsDirectory (), result );
81+ }
82+
83+ public void getStorageDirectory (@ NonNull Result result ) {
84+ executeInBackground (() -> getPathProviderStorageDirectory (), result );
85+ }
86+
87+ public void getExternalCacheDirectories (@ NonNull Result result ) {
88+ executeInBackground (() -> getPathProviderExternalCacheDirectories (), result );
89+ }
90+
91+ public void getExternalStorageDirectories (
92+ @ NonNull String directoryName , @ NonNull Result result ) {
93+ executeInBackground (() -> getPathProviderExternalStorageDirectories (directoryName ), result );
94+ }
95+
96+ public void getApplicationSupportDirectory (@ NonNull Result result ) {
97+ executeInBackground (() -> PathProviderPlugin .this .getApplicationSupportDirectory (), result );
98+ }
99+
100+ private <T > void executeInBackground (Callable <T > task , Result result ) {
101+ final SettableFuture <T > future = SettableFuture .create ();
102+ Futures .addCallback (
103+ future ,
104+ new FutureCallback <T >() {
105+ public void onSuccess (T answer ) {
106+ result .success (answer );
107+ }
108+
109+ public void onFailure (Throwable t ) {
110+ result .error (t .getClass ().getName (), t .getMessage (), null );
111+ }
112+ },
113+ uiThreadExecutor );
114+ executor .execute (
115+ () -> {
116+ try {
117+ future .set (task .call ());
118+ } catch (Throwable t ) {
119+ future .setException (t );
120+ }
121+ });
122+ }
123+ }
124+
125+ /** The implementation for getting system paths that executes from a background thread. */
126+ private class PathProviderBackgroundThread implements PathProviderImpl {
127+ public void getTemporaryDirectory (@ NonNull Result result ) {
128+ result .success (getPathProviderTemporaryDirectory ());
129+ }
130+
131+ public void getApplicationDocumentsDirectory (@ NonNull Result result ) {
132+ result .success (getPathProviderApplicationDocumentsDirectory ());
133+ }
134+
135+ public void getStorageDirectory (@ NonNull Result result ) {
136+ result .success (getPathProviderStorageDirectory ());
137+ }
138+
139+ public void getExternalCacheDirectories (@ NonNull Result result ) {
140+ result .success (getPathProviderExternalCacheDirectories ());
141+ }
142+
143+ public void getExternalStorageDirectories (
144+ @ NonNull String directoryName , @ NonNull Result result ) {
145+ result .success (getPathProviderExternalStorageDirectories (directoryName ));
146+ }
147+
148+ public void getApplicationSupportDirectory (@ NonNull Result result ) {
149+ result .success (PathProviderPlugin .this .getApplicationSupportDirectory ());
150+ }
151+ }
41152
42153 public PathProviderPlugin () {}
43154
155+ private void setup (BinaryMessenger messenger , Context context ) {
156+ String channelName = "plugins.flutter.io/path_provider" ;
157+ // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147
158+ // becomes available on the stable branch.
159+ try {
160+ Class methodChannelClass = Class .forName ("io.flutter.plugin.common.MethodChannel" );
161+ Class taskQueueClass = Class .forName ("io.flutter.plugin.common.BinaryMessenger$TaskQueue" );
162+ Method makeBackgroundTaskQueue = messenger .getClass ().getMethod ("makeBackgroundTaskQueue" );
163+ Object taskQueue = makeBackgroundTaskQueue .invoke (messenger );
164+ Constructor <MethodChannel > constructor =
165+ methodChannelClass .getConstructor (
166+ BinaryMessenger .class , String .class , MethodCodec .class , taskQueueClass );
167+ channel =
168+ constructor .newInstance (messenger , channelName , StandardMethodCodec .INSTANCE , taskQueue );
169+ impl = new PathProviderBackgroundThread ();
170+ Log .d (TAG , "Use TaskQueues." );
171+ } catch (Exception ex ) {
172+ channel = new MethodChannel (messenger , channelName );
173+ impl = new PathProviderPlatformThread ();
174+ Log .d (TAG , "Don't use TaskQueues." );
175+ }
176+ this .context = context ;
177+ channel .setMethodCallHandler (this );
178+ }
179+
44180 @ SuppressWarnings ("deprecation" )
45181 public static void registerWith (io .flutter .plugin .common .PluginRegistry .Registrar registrar ) {
46182 PathProviderPlugin instance = new PathProviderPlugin ();
47- instance .channel = new MethodChannel (registrar .messenger (), "plugins.flutter.io/path_provider" );
48- instance .context = registrar .context ();
49- instance .channel .setMethodCallHandler (instance );
183+ instance .setup (registrar .messenger (), registrar .context ());
50184 }
51185
52186 @ Override
53187 public void onAttachedToEngine (@ NonNull FlutterPluginBinding binding ) {
54- channel = new MethodChannel (binding .getBinaryMessenger (), "plugins.flutter.io/path_provider" );
55- context = binding .getApplicationContext ();
56- channel .setMethodCallHandler (this );
188+ setup (binding .getBinaryMessenger (), binding .getApplicationContext ());
57189 }
58190
59191 @ Override
@@ -62,52 +194,28 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
62194 channel = null ;
63195 }
64196
65- private <T > void executeInBackground (Callable <T > task , Result result ) {
66- final SettableFuture <T > future = SettableFuture .create ();
67- Futures .addCallback (
68- future ,
69- new FutureCallback <T >() {
70- public void onSuccess (T answer ) {
71- result .success (answer );
72- }
73-
74- public void onFailure (Throwable t ) {
75- result .error (t .getClass ().getName (), t .getMessage (), null );
76- }
77- },
78- uiThreadExecutor );
79- executor .execute (
80- () -> {
81- try {
82- future .set (task .call ());
83- } catch (Throwable t ) {
84- future .setException (t );
85- }
86- });
87- }
88-
89197 @ Override
90198 public void onMethodCall (MethodCall call , @ NonNull Result result ) {
91199 switch (call .method ) {
92200 case "getTemporaryDirectory" :
93- executeInBackground (() -> getPathProviderTemporaryDirectory (), result );
201+ impl . getTemporaryDirectory ( result );
94202 break ;
95203 case "getApplicationDocumentsDirectory" :
96- executeInBackground (() -> getPathProviderApplicationDocumentsDirectory (), result );
204+ impl . getApplicationDocumentsDirectory ( result );
97205 break ;
98206 case "getStorageDirectory" :
99- executeInBackground (() -> getPathProviderStorageDirectory (), result );
207+ impl . getStorageDirectory ( result );
100208 break ;
101209 case "getExternalCacheDirectories" :
102- executeInBackground (() -> getPathProviderExternalCacheDirectories (), result );
210+ impl . getExternalCacheDirectories ( result );
103211 break ;
104212 case "getExternalStorageDirectories" :
105213 final Integer type = call .argument ("type" );
106214 final String directoryName = StorageDirectoryMapper .androidType (type );
107- executeInBackground (() -> getPathProviderExternalStorageDirectories ( directoryName ) , result );
215+ impl . getExternalStorageDirectories ( directoryName , result );
108216 break ;
109217 case "getApplicationSupportDirectory" :
110- executeInBackground (() -> getApplicationSupportDirectory (), result );
218+ impl . getApplicationSupportDirectory (result );
111219 break ;
112220 default :
113221 result .notImplemented ();
0 commit comments