Skip to content

Custom Service notification #2703

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
merged 17 commits into from
Dec 9, 2022
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
8 changes: 8 additions & 0 deletions doc/source/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ the json module to encode and decode more complex data.

from os import environ
argument = environ.get('PYTHON_SERVICE_ARGUMENT', '')

To customize the notification icon, title, and text use three optional
arguments to service.start()::

service.start(mActivity, 'small_icon', 'title', 'content' , argument)

Where 'small_icon' is the name of an Android drawable or mipmap resource,
and 'title' and 'content' are strings in the notification.

Services support a range of options and interactions not yet
documented here but all accessible via calling other methods of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,30 +102,47 @@ protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument)

protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
String smallIconName = extras.getString("smallIconName");
String contentTitle = extras.getString("contentTitle");
String contentText = extras.getString("contentText");
Notification notification;
Context context = getApplicationContext();
Intent contextIntent = new Intent(context, PythonActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// Unspecified icon uses default.
int smallIconId = context.getApplicationInfo().icon;
if (!smallIconName.equals("")){
int resId = getResources().getIdentifier(smallIconName, "mipmap",
getPackageName());
if (resId ==0) {
resId = getResources().getIdentifier(smallIconName, "drawable",
getPackageName());
}
if (resId !=0) {
smallIconId = resId;
}
}

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// This constructor is deprecated
notification = new Notification(
context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
smallIconId, serviceTitle, 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, serviceTitle, serviceDescription, pIntent);
func.invoke(notification, context, contentTitle, contentText, pIntent);
} catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
}
} else {
// 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 = "Background Service"; //TODO: make this configurable
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a" + getServiceId();
String channelName = "Background Service" + getServiceId();
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);

Expand All @@ -135,10 +152,10 @@ protected void doStartForeground(Bundle extras) {
manager.createNotificationChannel(chan);

Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(serviceTitle);
builder.setContentText(serviceDescription);
builder.setContentTitle(contentTitle);
builder.setContentText(contentText);
builder.setContentIntent(pIntent);
builder.setSmallIcon(context.getApplicationInfo().icon);
builder.setSmallIcon(smallIconId);
notification = builder.build();
}
startForeground(getServiceId(), notification);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,49 @@ public int startType() {
protected int getServiceId() {
return {{ service_id }};
}

static private void _start(Context ctx, String smallIconName,
String contentTitle, String contentText,
String pythonServiceArgument) {
Intent intent = getDefaultIntent(ctx, smallIconName, contentTitle,
contentText, pythonServiceArgument);
ctx.startService(intent);
}

static public void start(Context ctx, String pythonServiceArgument) {
Intent intent = getDefaultIntent(ctx, pythonServiceArgument);
ctx.startService(intent);
_start(ctx, "", "{{ args.name }}", "{{ name|capitalize }}", pythonServiceArgument);
}

static public Intent getDefaultIntent(Context ctx, String pythonServiceArgument) {
static public void start(Context ctx, String smallIconName,
String contentTitle, String contentText,
String pythonServiceArgument) {
_start(ctx, smallIconName, contentTitle, contentText, pythonServiceArgument);
}

static public Intent getDefaultIntent(Context ctx, String smallIconName,
String contentTitle, String contentText,
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);
intent.putExtra("smallIconName", smallIconName);
intent.putExtra("contentTitle", contentTitle);
intent.putExtra("contentText", contentText);
return intent;
}

@Override
protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) {
return Service{{ name|capitalize }}.getDefaultIntent(ctx, pythonServiceArgument);
return Service{{ name|capitalize }}.getDefaultIntent(ctx, "", "", "",
pythonServiceArgument);
}

static public void stop(Context ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ public static void prepare(Context ctx) {
PythonUtil.unpackAsset(ctx, "private", app_root_file, true);
PythonUtil.unpackPyBundle(ctx, ctx.getApplicationInfo().nativeLibraryDir + "/" + "libpybundle", app_root_file, false);
}

public static void start(Context ctx, String pythonServiceArgument) {
Intent intent = getDefaultIntent(ctx, pythonServiceArgument);


static private void _start(Context ctx, String smallIconName,
String contentTitle,
String contentText,
String pythonServiceArgument) {
Intent intent = getDefaultIntent(ctx, smallIconName, contentTitle,
contentText, pythonServiceArgument);
//foreground: {{foreground}}
{% if foreground %}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Expand All @@ -50,26 +53,43 @@ public static void start(Context ctx, String pythonServiceArgument) {
{% endif %}
}

static public Intent getDefaultIntent(Context ctx, String pythonServiceArgument) {
public static void start(Context ctx, String pythonServiceArgument) {
_start(ctx, "", "{{ args.name }}", "{{ name|capitalize }}", pythonServiceArgument);
}

static public void start(Context ctx, String smallIconName,
String contentTitle,
String contentText,
String pythonServiceArgument) {
_start(ctx, smallIconName, contentTitle, contentText, pythonServiceArgument);
}

static public Intent getDefaultIntent(Context ctx, String smallIconName,
String contentTitle,
String contentText,
String pythonServiceArgument) {
String appRoot = PythonUtil.getAppRoot(ctx);
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
intent.putExtra("androidPrivate", appRoot);
intent.putExtra("androidArgument", appRoot);
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
intent.putExtra("serviceTitle", "{{ name|capitalize }}");
intent.putExtra("serviceDescription", "");
intent.putExtra("pythonName", "{{ name }}");
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
intent.putExtra("pythonHome", appRoot);
intent.putExtra("androidUnpack", appRoot);
intent.putExtra("pythonPath", appRoot + ":" + appRoot + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
intent.putExtra("smallIconName", smallIconName);
intent.putExtra("contentTitle", contentTitle);
intent.putExtra("contentText", contentText);
return intent;
}

@Override
protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) {
return Service{{ name|capitalize }}.getDefaultIntent(ctx, pythonServiceArgument);
return Service{{ name|capitalize }}.getDefaultIntent(ctx, "", "", "",
pythonServiceArgument);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,40 @@ public static void start(Context ctx, String pythonServiceArgument) {
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
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");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
intent.putExtra("smallIconName", "");
intent.putExtra("contentTitle", "{{ name|capitalize }}");
intent.putExtra("contentText", "");
ctx.startService(intent);
}


public static void start(Context ctx, String smallIconName,
String contentTitle,
String contentText,
String pythonServiceArgument) {
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
intent.putExtra("androidPrivate", argument);
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
intent.putExtra("serviceTitle", "{{ name|capitalize }}");
intent.putExtra("pythonName", "{{ name }}");
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
intent.putExtra("pythonHome", argument);
intent.putExtra("androidUnpack", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
intent.putExtra("smallIconName", smallIconName);
intent.putExtra("contentTitle", contentTitle);
intent.putExtra("contentText", contentText);
ctx.startService(intent);
}

public static void stop(Context ctx) {
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
ctx.stopService(intent);
Expand Down