flet-android-background 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/PKG-INFO +8 -9
- flet_android_background-0.0.3/README.md +18 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/pyproject.toml +1 -1
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flet_android_background/__init__.py +15 -2
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flet_android_background/service.py +8 -2
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/android/src/main/AndroidManifest.xml +1 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/android/src/main/java/dev/alexstoica/background/BackgroundPlugin.java +2 -1
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/android/src/main/java/dev/alexstoica/background/TaskService.java +19 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/pubspec.yaml +1 -1
- flet_android_background-0.0.2/README.md +0 -19
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/.gitignore +0 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/LICENSE +0 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flet_android_background/android.py +0 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/android/build.gradle +0 -0
- {flet_android_background-0.0.2 → flet_android_background-0.0.3}/src/flutter/flet_android_background/lib/flet_android_background.dart +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-android-background
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Experimental foreground tasks for Flet on Android
|
|
5
5
|
Project-URL: Homepage, https://github.com/alex-stoica/flet-android-background
|
|
6
6
|
Project-URL: Repository, https://github.com/alex-stoica/flet-android-background
|
|
@@ -15,14 +15,14 @@ Description-Content-Type: text/markdown
|
|
|
15
15
|
|
|
16
16
|
# flet-android-background
|
|
17
17
|
|
|
18
|
-
Experimental
|
|
19
|
-
Locking permits continued execution; Android restrictions still apply.
|
|
20
|
-
Dismissal or runtime loss stops the service. Process death has no recovery.
|
|
18
|
+
Experimental; no process-death recovery. Pip install.
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
`await task.start(work)`
|
|
20
|
+
Create `BackgroundTask` per page; set `page.on_close = task.on_session_close`.
|
|
21
|
+
Use `await task.start(work, enable_wifi_lock=True)` and `await task.stop()`.
|
|
22
|
+
WifiLock: default off; released on exit. Android 14+: foreground/screen-on only;
|
|
23
|
+
Doze applies. Bundles `WAKE_LOCK`; no CPU lock.
|
|
24
24
|
|
|
25
|
-
Before building
|
|
25
|
+
Before building:
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
28
|
from flet_android_background.android import configure_android_project
|
|
@@ -30,5 +30,4 @@ from flet_android_background.android import configure_android_project
|
|
|
30
30
|
configure_android_project(path, service_type="dataSync")
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
an app-specific `subtype` explanation. Android time limits apply.
|
|
33
|
+
Types: `dataSync`, `shortService`, `specialUse` (requires `subtype`). Time limits apply.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# flet-android-background
|
|
2
|
+
|
|
3
|
+
Experimental; no process-death recovery. Pip install.
|
|
4
|
+
|
|
5
|
+
Create `BackgroundTask` per page; set `page.on_close = task.on_session_close`.
|
|
6
|
+
Use `await task.start(work, enable_wifi_lock=True)` and `await task.stop()`.
|
|
7
|
+
WifiLock: default off; released on exit. Android 14+: foreground/screen-on only;
|
|
8
|
+
Doze applies. Bundles `WAKE_LOCK`; no CPU lock.
|
|
9
|
+
|
|
10
|
+
Before building:
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from flet_android_background.android import configure_android_project
|
|
14
|
+
|
|
15
|
+
configure_android_project(path, service_type="dataSync")
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Types: `dataSync`, `shortService`, `specialUse` (requires `subtype`). Time limits apply.
|
|
@@ -16,6 +16,7 @@ class BackgroundTask:
|
|
|
16
16
|
self._task: asyncio.Task[None] | None = None
|
|
17
17
|
self._work: asyncio.Task[None] | None = None
|
|
18
18
|
self._lock = asyncio.Lock()
|
|
19
|
+
self._session_closed = False
|
|
19
20
|
|
|
20
21
|
@property
|
|
21
22
|
def running(self) -> bool:
|
|
@@ -25,6 +26,7 @@ class BackgroundTask:
|
|
|
25
26
|
**notification) -> bool:
|
|
26
27
|
"""Start once after notification permission; duplicates return False."""
|
|
27
28
|
async with self._lock:
|
|
29
|
+
self._check_session()
|
|
28
30
|
if self.running:
|
|
29
31
|
return False
|
|
30
32
|
self.error = None
|
|
@@ -35,6 +37,7 @@ class BackgroundTask:
|
|
|
35
37
|
self._task = asyncio.create_task(self._rollback(starting))
|
|
36
38
|
await asyncio.shield(self._task)
|
|
37
39
|
raise
|
|
40
|
+
self._check_session()
|
|
38
41
|
if failure is not None:
|
|
39
42
|
raise failure
|
|
40
43
|
self._work = asyncio.create_task(self._invoke(work))
|
|
@@ -53,7 +56,7 @@ class BackgroundTask:
|
|
|
53
56
|
async def _rollback(self, starting):
|
|
54
57
|
try:
|
|
55
58
|
self.error = await starting
|
|
56
|
-
if self.error is None:
|
|
59
|
+
if self.error is None and not self._session_closed:
|
|
57
60
|
await self.service.stop_foreground_service()
|
|
58
61
|
except Exception as exc:
|
|
59
62
|
self.error = exc
|
|
@@ -73,7 +76,8 @@ class BackgroundTask:
|
|
|
73
76
|
self.error = self.error or exc
|
|
74
77
|
finally:
|
|
75
78
|
try:
|
|
76
|
-
|
|
79
|
+
if not self._session_closed:
|
|
80
|
+
await self.service.stop_foreground_service()
|
|
77
81
|
except Exception as exc:
|
|
78
82
|
self.error = self.error or exc
|
|
79
83
|
|
|
@@ -85,6 +89,15 @@ class BackgroundTask:
|
|
|
85
89
|
self._work.cancel()
|
|
86
90
|
await asyncio.shield(self._task)
|
|
87
91
|
|
|
92
|
+
def _check_session(self):
|
|
93
|
+
if self._session_closed:
|
|
94
|
+
raise RuntimeError("Flet session closed; create a new BackgroundTask")
|
|
95
|
+
|
|
96
|
+
async def on_session_close(self, event=None):
|
|
97
|
+
"""Cancel Python work after session loss; native teardown stops the service."""
|
|
98
|
+
self._session_closed = True
|
|
99
|
+
await self.stop()
|
|
100
|
+
|
|
88
101
|
async def _stopped(self, event):
|
|
89
102
|
async with self._lock:
|
|
90
103
|
if event.data == self.service._owner and self.running:
|
|
@@ -16,10 +16,16 @@ class ForegroundService(ft.Service):
|
|
|
16
16
|
return await self._call("permission")
|
|
17
17
|
|
|
18
18
|
async def start_foreground_service(self, *, notification_id=1,
|
|
19
|
-
title="Background task", body="Running"
|
|
19
|
+
title="Background task", body="Running",
|
|
20
|
+
enable_wifi_lock: bool = False):
|
|
20
21
|
if notification_id <= 0:
|
|
21
22
|
raise ValueError("notification_id must be positive")
|
|
22
|
-
|
|
23
|
+
if not isinstance(enable_wifi_lock, bool):
|
|
24
|
+
raise TypeError("enable_wifi_lock must be a bool")
|
|
25
|
+
self._owner = await self._call(
|
|
26
|
+
"start", id=notification_id, title=title, body=body,
|
|
27
|
+
enable_wifi_lock=enable_wifi_lock,
|
|
28
|
+
)
|
|
23
29
|
|
|
24
30
|
async def stop_foreground_service(self):
|
|
25
31
|
await self._call("stop")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
2
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
3
3
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
4
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
4
5
|
<application>
|
|
5
6
|
<service android:name="dev.alexstoica.background.TaskService"
|
|
6
7
|
android:exported="false" />
|
|
@@ -53,7 +53,8 @@ public class BackgroundPlugin implements FlutterPlugin, ActivityAware,
|
|
|
53
53
|
Intent intent = new Intent(context, TaskService.class)
|
|
54
54
|
.putExtra("id", ((Number) call.argument("id")).intValue())
|
|
55
55
|
.putExtra("title", (String) call.argument("title"))
|
|
56
|
-
.putExtra("body", (String) call.argument("body"))
|
|
56
|
+
.putExtra("body", (String) call.argument("body"))
|
|
57
|
+
.putExtra("enable_wifi_lock", Boolean.TRUE.equals(call.argument("enable_wifi_lock")));
|
|
57
58
|
token = call.argument("owner");
|
|
58
59
|
if (token == null) throw new IllegalArgumentException("Missing service owner");
|
|
59
60
|
TaskService.owner = this;
|
|
@@ -7,11 +7,13 @@ import android.app.PendingIntent;
|
|
|
7
7
|
import android.app.Service;
|
|
8
8
|
import android.content.Intent;
|
|
9
9
|
import android.os.Build;
|
|
10
|
+
import android.net.wifi.WifiManager;
|
|
10
11
|
import android.os.IBinder;
|
|
11
12
|
|
|
12
13
|
public class TaskService extends Service {
|
|
13
14
|
static BackgroundPlugin owner;
|
|
14
15
|
private static final String CHANNEL = "python_background";
|
|
16
|
+
private WifiManager.WifiLock wifiLock;
|
|
15
17
|
|
|
16
18
|
@Override public int onStartCommand(Intent intent, int flags, int startId) {
|
|
17
19
|
if (owner == null || intent == null || "stop".equals(intent.getAction())) {
|
|
@@ -38,14 +40,30 @@ public class TaskService extends Service {
|
|
|
38
40
|
if (launch != null) builder.setContentIntent(PendingIntent.getActivity(
|
|
39
41
|
this, 0, launch, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT));
|
|
40
42
|
startForeground(intent.getIntExtra("id", 1), builder.build());
|
|
43
|
+
if (intent.getBooleanExtra("enable_wifi_lock", false) && wifiLock == null) {
|
|
44
|
+
WifiManager wifi = getApplicationContext().getSystemService(WifiManager.class);
|
|
45
|
+
if (wifi == null) throw new IllegalStateException("Wi-Fi service unavailable");
|
|
46
|
+
wifiLock = wifi.createWifiLock(
|
|
47
|
+
WifiManager.WIFI_MODE_FULL_HIGH_PERF, "flet_android_background:wifi");
|
|
48
|
+
wifiLock.setReferenceCounted(false);
|
|
49
|
+
wifiLock.acquire();
|
|
50
|
+
}
|
|
41
51
|
} catch (Exception error) {
|
|
42
52
|
failure = error;
|
|
53
|
+
releaseWifiLock();
|
|
43
54
|
stopSelf();
|
|
44
55
|
}
|
|
45
56
|
owner.onStarted(failure);
|
|
46
57
|
return START_NOT_STICKY;
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
private void releaseWifiLock() {
|
|
61
|
+
if (wifiLock != null) {
|
|
62
|
+
if (wifiLock.isHeld()) wifiLock.release();
|
|
63
|
+
wifiLock = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
@Override public void onTaskRemoved(Intent intent) { stopSelf(); }
|
|
50
68
|
@Override public void onTimeout(int startId) { stopSelf(); }
|
|
51
69
|
@Override public void onTimeout(int startId, int type) { stopSelf(); }
|
|
@@ -53,6 +71,7 @@ public class TaskService extends Service {
|
|
|
53
71
|
@Override public void onDestroy() {
|
|
54
72
|
BackgroundPlugin previous = owner;
|
|
55
73
|
owner = null;
|
|
74
|
+
releaseWifiLock();
|
|
56
75
|
stopForeground(STOP_FOREGROUND_REMOVE);
|
|
57
76
|
if (previous != null) previous.onStopped();
|
|
58
77
|
super.onDestroy();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# flet-android-background
|
|
2
|
-
|
|
3
|
-
Experimental Flet Android tasks; one owns the foreground service.
|
|
4
|
-
Locking permits continued execution; Android restrictions still apply.
|
|
5
|
-
Dismissal or runtime loss stops the service. Process death has no recovery.
|
|
6
|
-
|
|
7
|
-
Install the wheel with pip. Add `BackgroundTask` to your Flet app, then
|
|
8
|
-
`await task.start(work)` or `await task.stop()`.
|
|
9
|
-
|
|
10
|
-
Before building the generated Flutter project:
|
|
11
|
-
|
|
12
|
-
```python
|
|
13
|
-
from flet_android_background.android import configure_android_project
|
|
14
|
-
|
|
15
|
-
configure_android_project(path, service_type="dataSync")
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Supported types: `dataSync`, `shortService`, `specialUse`. The latter requires
|
|
19
|
-
an app-specific `subtype` explanation. Android time limits apply.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|