Skip to content

Commit db6e735

Browse files
author
Jonas Thiem
committed
Fix foreground notification being mandatory and more. Details:
- Adds start_service_not_as_foreground() to avoid always displaying an associated notification without this being configurable at runtime. It only is configurable right now if setting the foreground property via --service, which cannot be done when using the simpler service/main.py entrypoint - Fixes service_only service code template not rendering .foreground correctly since it added an outdated, useless function name override - Fixes notification channel name which is visible to end user hardcoding "python" in its name which is not ideal for end user naming (since the average user might not even know what python is) - Fixes override of doStartForeground() leading to quite some error-prone code duplication
1 parent 8cf23aa commit db6e735

File tree

9 files changed

+134
-103
lines changed

9 files changed

+134
-103
lines changed

pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ public class PythonService extends Service implements Runnable {
3535
private String pythonHome;
3636
private String pythonPath;
3737
private String serviceEntrypoint;
38+
private boolean serviceStartAsForeground;
3839
// Argument to pass to Python code,
3940
private String pythonServiceArgument;
41+
42+
4043
public static PythonService mService = null;
4144
private Intent startIntent = null;
4245

@@ -46,10 +49,6 @@ public void setAutoRestartService(boolean restart) {
4649
autoRestartService = restart;
4750
}
4851

49-
public boolean canDisplayNotification() {
50-
return true;
51-
}
52-
5352
public int startType() {
5453
return START_NOT_STICKY;
5554
}
@@ -79,18 +78,24 @@ public int onStartCommand(Intent intent, int flags, int startId) {
7978
pythonName = extras.getString("pythonName");
8079
pythonHome = extras.getString("pythonHome");
8180
pythonPath = extras.getString("pythonPath");
81+
serviceStartAsForeground = (
82+
extras.getString("serviceStartAsForeground") == "true"
83+
);
8284
pythonServiceArgument = extras.getString("pythonServiceArgument");
83-
8485
pythonThread = new Thread(this);
8586
pythonThread.start();
8687

87-
if (canDisplayNotification()) {
88+
if (serviceStartAsForeground) {
8889
doStartForeground(extras);
8990
}
9091

9192
return startType();
9293
}
9394

95+
protected int getServiceId() {
96+
return 1;
97+
}
98+
9499
protected void doStartForeground(Bundle extras) {
95100
String serviceTitle = extras.getString("serviceTitle");
96101
String serviceDescription = extras.getString("serviceDescription");
@@ -116,7 +121,7 @@ protected void doStartForeground(Bundle extras) {
116121
// for android 8+ we need to create our own channel
117122
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
118123
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
119-
String channelName = "PythonSerice"; //TODO: make this configurable
124+
String channelName = "Background Service"; //TODO: make this configurable
120125
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
121126
NotificationManager.IMPORTANCE_NONE);
122127

@@ -132,7 +137,7 @@ protected void doStartForeground(Bundle extras) {
132137
builder.setSmallIcon(context.getApplicationInfo().icon);
133138
notification = builder.build();
134139
}
135-
startForeground(1, notification);
140+
startForeground(getServiceId(), notification);
136141
}
137142

138143
@Override

pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
package {{ args.package }};
22

3-
import android.os.Build;
4-
import java.lang.reflect.Method;
5-
import java.lang.reflect.InvocationTargetException;
63
import android.content.Intent;
74
import android.content.Context;
8-
import android.app.Notification;
9-
import android.app.PendingIntent;
10-
import android.os.Bundle;
115
import org.kivy.android.PythonService;
12-
import org.kivy.android.PythonActivity;
136

147

158
public class Service{{ name|capitalize }} extends PythonService {
@@ -20,50 +13,21 @@ public int startType() {
2013
}
2114
{% endif %}
2215

23-
{% if not foreground %}
2416
@Override
25-
public boolean canDisplayNotification() {
26-
return false;
27-
}
28-
{% endif %}
29-
30-
@Override
31-
protected void doStartForeground(Bundle extras) {
32-
Notification notification;
33-
Context context = getApplicationContext();
34-
Intent contextIntent = new Intent(context, PythonActivity.class);
35-
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
36-
PendingIntent.FLAG_UPDATE_CURRENT);
37-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
38-
notification = new Notification(
39-
context.getApplicationInfo().icon, "{{ args.name }}", System.currentTimeMillis());
40-
try {
41-
// prevent using NotificationCompat, this saves 100kb on apk
42-
Method func = notification.getClass().getMethod(
43-
"setLatestEventInfo", Context.class, CharSequence.class,
44-
CharSequence.class, PendingIntent.class);
45-
func.invoke(notification, context, "{{ args.name }}", "{{ name| capitalize }}", pIntent);
46-
} catch (NoSuchMethodException | IllegalAccessException |
47-
IllegalArgumentException | InvocationTargetException e) {
48-
}
49-
} else {
50-
Notification.Builder builder = new Notification.Builder(context);
51-
builder.setContentTitle("{{ args.name }}");
52-
builder.setContentText("{{ name| capitalize }}");
53-
builder.setContentIntent(pIntent);
54-
builder.setSmallIcon(context.getApplicationInfo().icon);
55-
notification = builder.build();
56-
}
57-
startForeground({{ service_id }}, notification);
17+
protected int getServiceId() {
18+
return {{ service_id }};
5819
}
5920

6021
static public void start(Context ctx, String pythonServiceArgument) {
6122
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
6223
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
6324
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
6425
intent.putExtra("androidArgument", argument);
26+
intent.putExtra("serviceTitle", "{{ args.name }}");
27+
intent.putExtra("serviceDescription", "{{ name|capitalize }}");
6528
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
6629
intent.putExtra("pythonName", "{{ name }}");
30+
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
6731
intent.putExtra("pythonHome", argument);
6832
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
6933
intent.putExtra("pythonServiceArgument", pythonServiceArgument);

pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
365365
}
366366
}
367367

368-
public static void start_service(String serviceTitle, String serviceDescription,
369-
String pythonServiceArgument) {
368+
public static void start_service(
369+
String serviceTitle,
370+
String serviceDescription,
371+
String pythonServiceArgument
372+
) {
373+
_do_start_service(
374+
serviceTitle, serviceDescription, pythonServiceArgument, true
375+
);
376+
}
377+
378+
public static void start_service_not_as_foreground(
379+
String serviceTitle,
380+
String serviceDescription,
381+
String pythonServiceArgument
382+
) {
383+
_do_start_service(
384+
serviceTitle, serviceDescription, pythonServiceArgument, false
385+
);
386+
}
387+
388+
public static void _do_start_service(
389+
String serviceTitle,
390+
String serviceDescription,
391+
String pythonServiceArgument,
392+
boolean showForegroundNotification
393+
) {
370394
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
371395
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
372396
String filesDirectory = argument;
@@ -378,6 +402,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
378402
serviceIntent.putExtra("pythonName", "python");
379403
serviceIntent.putExtra("pythonHome", app_root_dir);
380404
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
405+
serviceIntent.putExtra("serviceStartAsForeground",
406+
(showForegroundNotification ? "true" : "false")
407+
);
381408
serviceIntent.putExtra("serviceTitle", serviceTitle);
382409
serviceIntent.putExtra("serviceDescription", serviceDescription);
383410
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);

pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
380380
}
381381
}
382382

383-
public static void start_service(String serviceTitle, String serviceDescription,
384-
String pythonServiceArgument) {
383+
public static void start_service(
384+
String serviceTitle,
385+
String serviceDescription,
386+
String pythonServiceArgument
387+
) {
388+
_do_start_service(
389+
serviceTitle, serviceDescription, pythonServiceArgument, true
390+
);
391+
}
392+
393+
public static void start_service_not_as_foreground(
394+
String serviceTitle,
395+
String serviceDescription,
396+
String pythonServiceArgument
397+
) {
398+
_do_start_service(
399+
serviceTitle, serviceDescription, pythonServiceArgument, false
400+
);
401+
}
402+
403+
public static void _do_start_service(
404+
String serviceTitle,
405+
String serviceDescription,
406+
String pythonServiceArgument,
407+
boolean showForegroundNotification
408+
) {
385409
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
386410
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
387411
String filesDirectory = argument;
@@ -393,6 +417,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
393417
serviceIntent.putExtra("pythonName", "python");
394418
serviceIntent.putExtra("pythonHome", app_root_dir);
395419
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
420+
serviceIntent.putExtra("serviceStartAsForeground",
421+
(showForegroundNotification ? "true" : "false")
422+
);
396423
serviceIntent.putExtra("serviceTitle", serviceTitle);
397424
serviceIntent.putExtra("serviceDescription", serviceDescription);
398425
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);

pythonforandroid/bootstraps/service_only/build/templates/Service.tmpl.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,10 @@ public int startType() {
2222
}
2323
{% endif %}
2424

25-
{% if foreground %}
26-
/**
27-
* {@inheritDoc}
28-
*/
2925
@Override
30-
public boolean getStartForeground() {
31-
return true;
26+
protected int getServiceId() {
27+
return {{ service_id }};
3228
}
33-
{% endif %}
3429

3530
public static void start(Context ctx, String pythonServiceArgument) {
3631
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
@@ -41,6 +36,7 @@ public static void start(Context ctx, String pythonServiceArgument) {
4136
intent.putExtra("serviceTitle", "{{ name|capitalize }}");
4237
intent.putExtra("serviceDescription", "");
4338
intent.putExtra("pythonName", "{{ name }}");
39+
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
4440
intent.putExtra("pythonHome", argument);
4541
intent.putExtra("androidUnpack", argument);
4642
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");

pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
437437
}
438438
}
439439

440-
public static void start_service(String serviceTitle, String serviceDescription,
441-
String pythonServiceArgument) {
440+
public static void start_service(
441+
String serviceTitle,
442+
String serviceDescription,
443+
String pythonServiceArgument
444+
) {
445+
_do_start_service(
446+
serviceTitle, serviceDescription, pythonServiceArgument, true
447+
);
448+
}
449+
450+
public static void start_service_not_as_foreground(
451+
String serviceTitle,
452+
String serviceDescription,
453+
String pythonServiceArgument
454+
) {
455+
_do_start_service(
456+
serviceTitle, serviceDescription, pythonServiceArgument, false
457+
);
458+
}
459+
460+
public static void _do_start_service(
461+
String serviceTitle,
462+
String serviceDescription,
463+
String pythonServiceArgument,
464+
boolean showForegroundNotification
465+
) {
442466
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
443467
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
444468
String filesDirectory = argument;
@@ -450,6 +474,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
450474
serviceIntent.putExtra("pythonName", "python");
451475
serviceIntent.putExtra("pythonHome", app_root_dir);
452476
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
477+
serviceIntent.putExtra("serviceStartAsForeground",
478+
(showForegroundNotification ? "true" : "false")
479+
);
453480
serviceIntent.putExtra("serviceTitle", serviceTitle);
454481
serviceIntent.putExtra("serviceDescription", serviceDescription);
455482
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);

pythonforandroid/recipes/android/src/android/_android.pyx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -280,17 +280,29 @@ class AndroidBrowser(object):
280280
import webbrowser
281281
webbrowser.register('android', AndroidBrowser)
282282

283-
cdef extern void android_start_service(char *, char *, char *)
284-
def start_service(title=None, description=None, arg=None):
285-
cdef char *j_title = NULL
286-
cdef char *j_description = NULL
287-
if title is not None:
288-
j_title = <bytes>title
289-
if description is not None:
290-
j_description = <bytes>description
291-
if arg is not None:
292-
j_arg = <bytes>arg
293-
android_start_service(j_title, j_description, j_arg)
283+
284+
def start_service(title="Background Service",
285+
description="", arg="",
286+
as_foreground=True):
287+
# Legacy None value support (for old function signature style):
288+
if title is None:
289+
title = "Background Service"
290+
if description is None:
291+
description = ""
292+
if arg is None:
293+
arg = ""
294+
295+
# Start service:
296+
mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
297+
if as_foreground:
298+
mActivity.start_service(
299+
title, description, arg
300+
)
301+
else:
302+
mActivity.start_service_not_as_foreground(
303+
title, description, arg
304+
)
305+
294306

295307
cdef extern void android_stop_service()
296308
def stop_service():

pythonforandroid/recipes/android/src/android/_android_jni.c

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -201,34 +201,6 @@ void android_get_buildinfo() {
201201
}
202202
}
203203

204-
void android_start_service(char *title, char *description, char *arg) {
205-
static JNIEnv *env = NULL;
206-
static jclass *cls = NULL;
207-
static jmethodID mid = NULL;
208-
209-
if (env == NULL) {
210-
env = SDL_ANDROID_GetJNIEnv();
211-
aassert(env);
212-
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
213-
aassert(cls);
214-
mid = (*env)->GetStaticMethodID(env, cls, "start_service",
215-
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
216-
aassert(mid);
217-
}
218-
219-
jstring j_title = NULL;
220-
jstring j_description = NULL;
221-
jstring j_arg = NULL;
222-
if ( title != 0 )
223-
j_title = (*env)->NewStringUTF(env, title);
224-
if ( description != 0 )
225-
j_description = (*env)->NewStringUTF(env, description);
226-
if ( arg != 0 )
227-
j_arg = (*env)->NewStringUTF(env, arg);
228-
229-
(*env)->CallStaticVoidMethod(env, cls, mid, j_title, j_description, j_arg);
230-
}
231-
232204
void android_stop_service() {
233205
static JNIEnv *env = NULL;
234206
static jclass *cls = NULL;

tests/test_distribution.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,12 @@ def test_folder_exist(self, mock_exists):
8484
:meth:`~pythonforandroid.distribution.Distribution.folder_exist` is
8585
called once with the proper arguments."""
8686

87+
mock_exists.return_value = False
8788
self.setUp_distribution_with_bootstrap(
88-
Bootstrap().get_bootstrap("sdl2", self.ctx)
89+
Bootstrap.get_bootstrap("sdl2", self.ctx)
8990
)
9091
self.ctx.bootstrap.distribution.folder_exists()
91-
mock_exists.assert_called_once_with(
92+
mock_exists.assert_called_with(
9293
self.ctx.bootstrap.distribution.dist_dir
9394
)
9495

0 commit comments

Comments
 (0)