Skip to content

Commit 0063fac

Browse files
authored
Merge branch 'master' into add-text-column-db
2 parents ffda03c + 7873765 commit 0063fac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+622
-367
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.4.0 - 12.01.2020
2+
3+
* migrate to Android v2 embedding.
4+
15
## 1.3.4 - 21.12.2019
26

37
* fix bug stuck in Flutter v12.13

README.md

+9-75
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {
6464
// along with other plugins that need UI manipulation, you should register
6565
// `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
6666
//
67-
// [FlutterDownloaderPlugin registerWithRegistrar:[registry registrarForPlugin:@"vn.hunghd.flutter_downloader"]];
67+
// if (![registry hasPlugin:@"FlutterDownloaderPlugin"]) {
68+
// [FlutterDownloaderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterDownloaderPlugin"]];
69+
// }
6870
//
6971
[GeneratedPluginRegistrant registerWithRegistry:registry];
7072
}
@@ -113,7 +115,9 @@ private func registerPlugins(registry: FlutterPluginRegistry) {
113115
// plugins that need UI manipulation, you should register `FlutterDownloaderPlugin` and any
114116
// 'background' plugins explicitly like this:
115117
//
116-
// FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "vn.hunghd.flutter_downloader"))
118+
// if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
119+
// FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin"))
120+
// }
117121
//
118122
GeneratedPluginRegistrant.register(with: registry)
119123
}
@@ -179,78 +183,9 @@ private func registerPlugins(registry: FlutterPluginRegistry) {
179183

180184
### Required configuration:
181185

182-
* Configure `Application`:
183-
184-
Java:
185-
```java
186-
// MyApplication.java (create this file if it doesn't exist in your project)
187-
188-
import io.flutter.app.FlutterApplication;
189-
import io.flutter.plugin.common.PluginRegistry;
190-
import io.flutter.plugins.GeneratedPluginRegistrant;
191-
import vn.hunghd.flutterdownloader.FlutterDownloaderPlugin;
192-
193-
public class MyApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
194-
@Override
195-
public void registerWith(PluginRegistry registry) {
196-
//
197-
// Integration note:
198-
//
199-
// In Flutter, in order to work in background isolate, plugins need to register with
200-
// a special instance of `FlutterEngine` that serves for background execution only.
201-
// Hence, all (and only) plugins that require background execution feature need to
202-
// call `registerWith` in this method.
203-
//
204-
// The default `GeneratedPluginRegistrant` will call `registerWith` of all plugins
205-
// integrated in your application. Hence, if you are using `FlutterDownloaderPlugin`
206-
// along with other plugins that need UI manipulation, you should register
207-
// `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
208-
//
209-
// FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"));
210-
//
211-
GeneratedPluginRegistrant.registerWith(registry);
212-
}
213-
}
214-
```
215-
216-
Or Kotlin:
217-
```kotlin
218-
// MyApplication.kt (create this file if it doesn't exist in your project)
219-
220-
import io.flutter.app.FlutterApplication
221-
import io.flutter.plugin.common.PluginRegistry
222-
import io.flutter.plugins.GeneratedPluginRegistrant
223-
import vn.hunghd.flutterdownloader.FlutterDownloaderPlugin
224-
225-
internal class MyApplication : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
226-
override fun registerWith(registry: PluginRegistry) {
227-
//
228-
// Integration note:
229-
//
230-
// In Flutter, in order to work in background isolate, plugins need to register with
231-
// a special instance of `FlutterEngine` that serves for background execution only.
232-
// Hence, all (and only) plugins that require background execution feature need to
233-
// call `registerWith` in this method.
234-
//
235-
// The default `GeneratedPluginRegistrant` will call `registerWith` of all plugins
236-
// integrated in your application. Hence, if you are using `FlutterDownloaderPlugin`
237-
// along with other plugins that need UI manipulation, you should register
238-
// `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
239-
//
240-
// FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"))
241-
//
242-
GeneratedPluginRegistrant.registerWith(registry)
243-
}
244-
}
245-
```
186+
* If your project is running on Flutter versions prior v1.12, have a look at [this document](android_integration_note.md) to configure your Android project.
246187

247-
And update `AndroidManifest.xml`
248-
```xml
249-
<!-- AndroidManifest.xml -->
250-
<application
251-
android:name=".MyApplication"
252-
....>
253-
```
188+
* From Flutter v1.12 with Android v2 embedding there's no additional configurations required to work with background isolation in Android.
254189

255190
* In order to handle click action on notification to open the downloaded file on Android, you need to add some additional configurations. Add the following codes to your `AndroidManifest.xml`:
256191

@@ -270,8 +205,6 @@ And update `AndroidManifest.xml`
270205
- You have to save your downloaded files in external storage (where the other applications have permission to read your files)
271206
- The downloaded files are only able to be opened if your device has at least an application that can read these file types (mp3, pdf, etc)
272207

273-
274-
275208
### Optional configuration:
276209

277210
* **Configure maximum number of concurrent tasks:** the plugin depends on `WorkManager` library and `WorkManager` depends on the number of available processor to configure the maximum number of tasks running at a moment. You can setup a fixed number for this configuration by adding following codes to your `AndroidManifest.xml`:
@@ -322,6 +255,7 @@ import 'package:flutter_downloader/flutter_downloader.dart';
322255
#### Initialize
323256

324257
````dart
258+
WidgetsFlutterBinding.ensureInitialized();
325259
await FlutterDownloader.initialize();
326260
````
327261

android/src/main/java/vn/hunghd/flutterdownloader/DownloadWorker.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,6 @@ public void run() {
103103
private void startBackgroundIsolate(Context context) {
104104
synchronized (isolateStarted) {
105105
if (backgroundFlutterView == null) {
106-
if (BuildConfig.DEBUG && !(getApplicationContext() instanceof PluginRegistrantCallback)) {
107-
throw new AssertionError("The Application must be implemented PluginRegistrantCallback");
108-
}
109-
PluginRegistrantCallback pluginRegistrantCallback = (PluginRegistrantCallback) getApplicationContext();
110-
111106
SharedPreferences pref = context.getSharedPreferences(FlutterDownloaderPlugin.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
112107
long callbackHandle = pref.getLong(FlutterDownloaderPlugin.CALLBACK_DISPATCHER_HANDLE_KEY, 0);
113108

@@ -121,8 +116,12 @@ private void startBackgroundIsolate(Context context) {
121116

122117
backgroundFlutterView = new FlutterNativeView(getApplicationContext(), true);
123118

124-
PluginRegistry registry = backgroundFlutterView.getPluginRegistry();
125-
pluginRegistrantCallback.registerWith(registry);
119+
/// backward compatibility with V1 embedding
120+
if (getApplicationContext() instanceof PluginRegistrantCallback) {
121+
PluginRegistrantCallback pluginRegistrantCallback = (PluginRegistrantCallback) getApplicationContext();
122+
PluginRegistry registry = backgroundFlutterView.getPluginRegistry();
123+
pluginRegistrantCallback.registerWith(registry);
124+
}
126125

127126
FlutterRunArguments args = new FlutterRunArguments();
128127
args.bundlePath = FlutterMain.findAppBundlePath(context);

android/src/main/java/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.java

+33-10
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,47 @@
2323
import androidx.work.WorkManager;
2424
import androidx.work.WorkRequest;
2525

26+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
2627
import io.flutter.plugin.common.BinaryMessenger;
2728
import io.flutter.plugin.common.MethodCall;
2829
import io.flutter.plugin.common.MethodChannel;
2930
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
3031
import io.flutter.plugin.common.PluginRegistry;
3132

32-
public class FlutterDownloaderPlugin implements MethodCallHandler {
33+
public class FlutterDownloaderPlugin implements MethodCallHandler, FlutterPlugin {
3334
private static final String CHANNEL = "vn.hunghd/downloader";
3435
private static final String TAG = "flutter_download_task";
3536

3637
public static final String SHARED_PREFERENCES_KEY = "vn.hunghd.downloader.pref";
3738
public static final String CALLBACK_DISPATCHER_HANDLE_KEY = "callback_dispatcher_handle_key";
3839

40+
private static FlutterDownloaderPlugin instance;
3941
private MethodChannel flutterChannel;
4042
private TaskDbHelper dbHelper;
4143
private TaskDao taskDao;
4244
private Context context;
4345
private long callbackHandle;
44-
45-
private FlutterDownloaderPlugin(Context context, BinaryMessenger messenger) {
46-
this.context = context;
47-
flutterChannel = new MethodChannel(messenger, CHANNEL);
48-
flutterChannel.setMethodCallHandler(this);
49-
dbHelper = TaskDbHelper.getInstance(context);
50-
taskDao = new TaskDao(dbHelper);
51-
}
46+
private final Object initializationLock = new Object();
5247

5348
@SuppressLint("NewApi")
5449
public static void registerWith(PluginRegistry.Registrar registrar) {
55-
new FlutterDownloaderPlugin(registrar.context(), registrar.messenger());
50+
if (instance == null) {
51+
instance = new FlutterDownloaderPlugin();
52+
}
53+
instance.onAttachedToEngine(registrar.context(), registrar.messenger());
54+
}
55+
56+
public void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) {
57+
synchronized (initializationLock) {
58+
if (flutterChannel != null) {
59+
return;
60+
}
61+
this.context = applicationContext;
62+
flutterChannel = new MethodChannel(messenger, CHANNEL);
63+
flutterChannel.setMethodCallHandler(this);
64+
dbHelper = TaskDbHelper.getInstance(context);
65+
taskDao = new TaskDao(dbHelper);
66+
}
5667
}
5768

5869
@Override
@@ -86,6 +97,18 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
8697
}
8798
}
8899

100+
@Override
101+
public void onAttachedToEngine(FlutterPluginBinding binding) {
102+
onAttachedToEngine(binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor());
103+
}
104+
105+
@Override
106+
public void onDetachedFromEngine(FlutterPluginBinding binding) {
107+
context = null;
108+
flutterChannel.setMethodCallHandler(null);
109+
flutterChannel = null;
110+
}
111+
89112
private WorkRequest buildRequest(String url, String savedDir, String filename, String additionalinfo, String headers, boolean showNotification, boolean openFileFromNotification, boolean isResume, boolean requiresStorageNotLow) {
90113
WorkRequest request = new OneTimeWorkRequest.Builder(DownloadWorker.class)
91114
.setConstraints(new Constraints.Builder()

android_integration_note.md

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
## Android integration
2+
3+
### Required configuration:
4+
5+
* Configure `Application`:
6+
7+
Java:
8+
```java
9+
// MyApplication.java (create this file if it doesn't exist in your project)
10+
11+
import io.flutter.app.FlutterApplication;
12+
import io.flutter.plugin.common.PluginRegistry;
13+
import io.flutter.plugins.GeneratedPluginRegistrant;
14+
import vn.hunghd.flutterdownloader.FlutterDownloaderPlugin;
15+
16+
public class MyApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
17+
@Override
18+
public void registerWith(PluginRegistry registry) {
19+
//
20+
// Integration note:
21+
//
22+
// In Flutter, in order to work in background isolate, plugins need to register with
23+
// a special instance of `FlutterEngine` that serves for background execution only.
24+
// Hence, all (and only) plugins that require background execution feature need to
25+
// call `registerWith` in this method.
26+
//
27+
// The default `GeneratedPluginRegistrant` will call `registerWith` of all plugins
28+
// integrated in your application. Hence, if you are using `FlutterDownloaderPlugin`
29+
// along with other plugins that need UI manipulation, you should register
30+
// `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
31+
//
32+
// if (!registry.hasPlugin("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin")) {
33+
// FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"));
34+
// }
35+
//
36+
GeneratedPluginRegistrant.registerWith(registry);
37+
}
38+
}
39+
```
40+
41+
Or Kotlin:
42+
```kotlin
43+
// MyApplication.kt (create this file if it doesn't exist in your project)
44+
45+
import io.flutter.app.FlutterApplication
46+
import io.flutter.plugin.common.PluginRegistry
47+
import io.flutter.plugins.GeneratedPluginRegistrant
48+
import vn.hunghd.flutterdownloader.FlutterDownloaderPlugin
49+
50+
internal class MyApplication : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
51+
override fun registerWith(registry: PluginRegistry) {
52+
//
53+
// Integration note:
54+
//
55+
// In Flutter, in order to work in background isolate, plugins need to register with
56+
// a special instance of `FlutterEngine` that serves for background execution only.
57+
// Hence, all (and only) plugins that require background execution feature need to
58+
// call `registerWith` in this method.
59+
//
60+
// The default `GeneratedPluginRegistrant` will call `registerWith` of all plugins
61+
// integrated in your application. Hence, if you are using `FlutterDownloaderPlugin`
62+
// along with other plugins that need UI manipulation, you should register
63+
// `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
64+
//
65+
// if (!registry.hasPlugin("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin")) {
66+
// FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"))
67+
// }
68+
//
69+
GeneratedPluginRegistrant.registerWith(registry)
70+
}
71+
}
72+
```
73+
74+
And update `AndroidManifest.xml`
75+
```xml
76+
<!-- AndroidManifest.xml -->
77+
<application
78+
android:name=".MyApplication"
79+
....>
80+
```
81+
82+
* In order to handle click action on notification to open the downloaded file on Android, you need to add some additional configurations. Add the following codes to your `AndroidManifest.xml`:
83+
84+
````xml
85+
<provider
86+
android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
87+
android:authorities="${applicationId}.flutter_downloader.provider"
88+
android:exported="false"
89+
android:grantUriPermissions="true">
90+
<meta-data
91+
android:name="android.support.FILE_PROVIDER_PATHS"
92+
android:resource="@xml/provider_paths"/>
93+
</provider>
94+
````
95+
96+
**Note:**
97+
- You have to save your downloaded files in external storage (where the other applications have permission to read your files)
98+
- The downloaded files are only able to be opened if your device has at least an application that can read these file types (mp3, pdf, etc)
99+
100+
101+
102+
### Optional configuration:
103+
104+
* **Configure maximum number of concurrent tasks:** the plugin depends on `WorkManager` library and `WorkManager` depends on the number of available processor to configure the maximum number of tasks running at a moment. You can setup a fixed number for this configuration by adding following codes to your `AndroidManifest.xml`:
105+
106+
````xml
107+
<provider
108+
android:name="androidx.work.impl.WorkManagerInitializer"
109+
android:authorities="${applicationId}.workmanager-init"
110+
android:enabled="false"
111+
android:exported="false" />
112+
113+
<provider
114+
android:name="vn.hunghd.flutterdownloader.FlutterDownloaderInitializer"
115+
android:authorities="${applicationId}.flutter-downloader-init"
116+
android:exported="false">
117+
<!-- changes this number to configure the maximum number of concurrent tasks -->
118+
<meta-data
119+
android:name="vn.hunghd.flutterdownloader.MAX_CONCURRENT_TASKS"
120+
android:value="5" />
121+
</provider>
122+
````
123+
124+
* **Localize notification messages:** you can localize notification messages of download progress by localizing following messages. (you can find the detail of string localization in Android in this [link][4])
125+
126+
````xml
127+
<string name="flutter_downloader_notification_started">Download started</string>
128+
<string name="flutter_downloader_notification_in_progress">Download in progress</string>
129+
<string name="flutter_downloader_notification_canceled">Download canceled</string>
130+
<string name="flutter_downloader_notification_failed">Download failed</string>
131+
<string name="flutter_downloader_notification_complete">Download complete</string>
132+
<string name="flutter_downloader_notification_paused">Download paused</string>
133+
````
134+
135+
* **PackageInstaller:** in order to open APK files, your application needs `REQUEST_INSTALL_PACKAGES` permission. Add following codes in your `AndroidManifest.xml`:
136+
137+
````xml
138+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
139+
````

example/android/.gitignore

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
*.iml
2-
*.class
3-
.gradle
1+
gradle-wrapper.jar
2+
/.gradle
3+
/captures/
4+
/gradlew
5+
/gradlew.bat
46
/local.properties
5-
/.idea/workspace.xml
6-
/.idea/libraries
7-
.DS_Store
8-
/build
9-
/captures
107
GeneratedPluginRegistrant.java

example/android/.idea/compiler.xml

-8
This file was deleted.

0 commit comments

Comments
 (0)