Skip to content

Fix foreground notification being mandatory and more (attempt 2) #1888

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
1 commit merged into from Jul 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ public class PythonService extends Service implements Runnable {
private String pythonHome;
private String pythonPath;
private String serviceEntrypoint;
private boolean serviceStartAsForeground;
// Argument to pass to Python code,
private String pythonServiceArgument;


public static PythonService mService = null;
private Intent startIntent = null;

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

public boolean canDisplayNotification() {
return true;
}

public int startType() {
return START_NOT_STICKY;
}
Expand Down Expand Up @@ -79,18 +78,24 @@ public int onStartCommand(Intent intent, int flags, int startId) {
pythonName = extras.getString("pythonName");
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
serviceStartAsForeground = (
extras.getString("serviceStartAsForeground") == "true"
);
pythonServiceArgument = extras.getString("pythonServiceArgument");

pythonThread = new Thread(this);
pythonThread.start();

if (canDisplayNotification()) {
if (serviceStartAsForeground) {
doStartForeground(extras);
}

return startType();
}

protected int getServiceId() {
return 1;
}

protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
Expand All @@ -116,7 +121,7 @@ protected void doStartForeground(Bundle extras) {
// for android 8+ we need to create our own channel
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
String channelName = "PythonSerice"; //TODO: make this configurable
String channelName = "Background Service"; //TODO: make this configurable
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);

Expand All @@ -132,7 +137,7 @@ protected void doStartForeground(Bundle extras) {
builder.setSmallIcon(context.getApplicationInfo().icon);
notification = builder.build();
}
startForeground(1, notification);
startForeground(getServiceId(), notification);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
package {{ args.package }};

import android.os.Build;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.content.Intent;
import android.content.Context;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Bundle;
import org.kivy.android.PythonService;
import org.kivy.android.PythonActivity;


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

{% if not foreground %}
@Override
public boolean canDisplayNotification() {
return false;
}
{% endif %}

@Override
protected void doStartForeground(Bundle extras) {
Notification notification;
Context context = getApplicationContext();
Intent contextIntent = new Intent(context, PythonActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
notification = new Notification(
context.getApplicationInfo().icon, "{{ args.name }}", System.currentTimeMillis());
try {
// prevent using NotificationCompat, this saves 100kb on apk
Method func = notification.getClass().getMethod(
"setLatestEventInfo", Context.class, CharSequence.class,
CharSequence.class, PendingIntent.class);
func.invoke(notification, context, "{{ args.name }}", "{{ name| capitalize }}", pIntent);
} catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
}
} else {
Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("{{ args.name }}");
builder.setContentText("{{ name| capitalize }}");
builder.setContentIntent(pIntent);
builder.setSmallIcon(context.getApplicationInfo().icon);
notification = builder.build();
}
startForeground({{ service_id }}, notification);
protected int getServiceId() {
return {{ service_id }};
}

static public void start(Context ctx, String pythonServiceArgument) {
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceTitle", "{{ args.name }}");
intent.putExtra("serviceDescription", "{{ name|capitalize }}");
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
intent.putExtra("pythonName", "{{ name }}");
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
intent.putExtra("pythonHome", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
}
}

public static void start_service(String serviceTitle, String serviceDescription,
String pythonServiceArgument) {
public static void start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, true
);
}

public static void start_service_not_as_foreground(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, false
);
}

public static void _do_start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument,
boolean showForegroundNotification
) {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
String filesDirectory = argument;
Expand All @@ -378,6 +402,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
serviceIntent.putExtra("pythonName", "python");
serviceIntent.putExtra("pythonHome", app_root_dir);
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
serviceIntent.putExtra("serviceStartAsForeground",
(showForegroundNotification ? "true" : "false")
);
serviceIntent.putExtra("serviceTitle", serviceTitle);
serviceIntent.putExtra("serviceDescription", serviceDescription);
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
}
}

public static void start_service(String serviceTitle, String serviceDescription,
String pythonServiceArgument) {
public static void start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, true
);
}

public static void start_service_not_as_foreground(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, false
);
}

public static void _do_start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument,
boolean showForegroundNotification
) {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
String filesDirectory = argument;
Expand All @@ -393,6 +417,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
serviceIntent.putExtra("pythonName", "python");
serviceIntent.putExtra("pythonHome", app_root_dir);
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
serviceIntent.putExtra("serviceStartAsForeground",
(showForegroundNotification ? "true" : "false")
);
serviceIntent.putExtra("serviceTitle", serviceTitle);
serviceIntent.putExtra("serviceDescription", serviceDescription);
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ public int startType() {
}
{% endif %}

{% if foreground %}
/**
* {@inheritDoc}
*/
@Override
public boolean getStartForeground() {
return true;
protected int getServiceId() {
return {{ service_id }};
}
{% endif %}

public static void start(Context ctx, String pythonServiceArgument) {
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
Expand All @@ -41,6 +36,7 @@ public static void start(Context ctx, String pythonServiceArgument) {
intent.putExtra("serviceTitle", "{{ name|capitalize }}");
intent.putExtra("serviceDescription", "");
intent.putExtra("pythonName", "{{ name }}");
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
intent.putExtra("pythonHome", argument);
intent.putExtra("androidUnpack", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,32 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
}
}

public static void start_service(String serviceTitle, String serviceDescription,
String pythonServiceArgument) {
public static void start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, true
);
}

public static void start_service_not_as_foreground(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument
) {
_do_start_service(
serviceTitle, serviceDescription, pythonServiceArgument, false
);
}

public static void _do_start_service(
String serviceTitle,
String serviceDescription,
String pythonServiceArgument,
boolean showForegroundNotification
) {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
String filesDirectory = argument;
Expand All @@ -450,6 +474,9 @@ public static void start_service(String serviceTitle, String serviceDescription,
serviceIntent.putExtra("pythonName", "python");
serviceIntent.putExtra("pythonHome", app_root_dir);
serviceIntent.putExtra("pythonPath", app_root_dir + ":" + app_root_dir + "/lib");
serviceIntent.putExtra("serviceStartAsForeground",
(showForegroundNotification ? "true" : "false")
);
serviceIntent.putExtra("serviceTitle", serviceTitle);
serviceIntent.putExtra("serviceDescription", serviceDescription);
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);
Expand Down
34 changes: 23 additions & 11 deletions pythonforandroid/recipes/android/src/android/_android.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,29 @@ class AndroidBrowser(object):
import webbrowser
webbrowser.register('android', AndroidBrowser)

cdef extern void android_start_service(char *, char *, char *)
def start_service(title=None, description=None, arg=None):
cdef char *j_title = NULL
cdef char *j_description = NULL
if title is not None:
j_title = <bytes>title
if description is not None:
j_description = <bytes>description
if arg is not None:
j_arg = <bytes>arg
android_start_service(j_title, j_description, j_arg)

def start_service(title="Background Service",
description="", arg="",
as_foreground=True):
# Legacy None value support (for old function signature style):
if title is None:
title = "Background Service"
if description is None:
description = ""
if arg is None:
arg = ""

# Start service:
mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also already available at module level.
https://github.com/kivy/python-for-android/blob/v2019.07.08/pythonforandroid/recipes/android/src/android/_android.pyx#L164
That probably requires some refactor though 😄

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I've kept the mActivity for now, seems a bit ugly to have that outside the function and rely on that being available

if as_foreground:
mActivity.start_service(
title, description, arg
)
else:
mActivity.start_service_not_as_foreground(
title, description, arg
)


cdef extern void android_stop_service()
def stop_service():
Expand Down
28 changes: 0 additions & 28 deletions pythonforandroid/recipes/android/src/android/_android_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,34 +201,6 @@ void android_get_buildinfo() {
}
}

void android_start_service(char *title, char *description, char *arg) {
static JNIEnv *env = NULL;
static jclass *cls = NULL;
static jmethodID mid = NULL;

if (env == NULL) {
env = SDL_ANDROID_GetJNIEnv();
aassert(env);
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
aassert(cls);
mid = (*env)->GetStaticMethodID(env, cls, "start_service",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
aassert(mid);
}

jstring j_title = NULL;
jstring j_description = NULL;
jstring j_arg = NULL;
if ( title != 0 )
j_title = (*env)->NewStringUTF(env, title);
if ( description != 0 )
j_description = (*env)->NewStringUTF(env, description);
if ( arg != 0 )
j_arg = (*env)->NewStringUTF(env, arg);

(*env)->CallStaticVoidMethod(env, cls, mid, j_title, j_description, j_arg);
}

void android_stop_service() {
static JNIEnv *env = NULL;
static jclass *cls = NULL;
Expand Down
5 changes: 3 additions & 2 deletions tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ def test_folder_exist(self, mock_exists):
:meth:`~pythonforandroid.distribution.Distribution.folder_exist` is
called once with the proper arguments."""

mock_exists.return_value = False
self.setUp_distribution_with_bootstrap(
Bootstrap().get_bootstrap("sdl2", self.ctx)
Bootstrap.get_bootstrap("sdl2", self.ctx)
)
self.ctx.bootstrap.distribution.folder_exists()
mock_exists.assert_called_once_with(
mock_exists.assert_called_with(
self.ctx.bootstrap.distribution.dist_dir
)

Expand Down