Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: koro666/notifikator
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: scottmconway/notifikator
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 10 commits
  • 16 files changed
  • 3 contributors

Commits on May 5, 2023

  1. Add support for gotify protocol (using token as a GET param in Gotify…

    … URL)
    
    Add a (presently static) package deny list
    scottmconway committed May 5, 2023
    1
    Copy the full SHA
    cd5c383 View commit details
  2. Copy the full SHA
    003ec8c View commit details

Commits on May 9, 2023

  1. update gradle and targetSdkVersion (WIP)

    still need to make some changes for the higher SDK version
    scottmconway committed May 9, 2023
    Copy the full SHA
    b0e2a5f View commit details

Commits on May 11, 2023

  1. Update to target SDK 33 (android 13)

    add notification channel for SDK >=26
    scottmconway committed May 11, 2023
    Copy the full SHA
    ea12d60 View commit details

Commits on Sep 24, 2023

  1. Added a preference to load a package denylist file and store results …

    …in shared preferences
    
    optimize imports (in touched files)
    scottmconway committed Sep 24, 2023
    Copy the full SHA
    09f5f56 View commit details
  2. Merge pull request #4 from scottmconway/custom_denylist

    Added custom package denylist MVP
    scottmconway authored Sep 24, 2023
    Copy the full SHA
    cdf65c7 View commit details
  3. update readme

    add `POST_NOTIFICATIONS` permission for android 13+
    scottmconway committed Sep 24, 2023
    Copy the full SHA
    b686cb6 View commit details

Commits on Aug 9, 2024

  1. Copy the full SHA
    c574fb0 View commit details

Commits on Jan 4, 2025

  1. allow sending plaintext HTTP requests to http:// urls; prompt user to…

    … allow POST_NOTIFICATIONS permissions on startup in order for "Send a test notification" feature to work
    artempavlov committed Jan 4, 2025
    Copy the full SHA
    e856a8d View commit details

Commits on Jan 6, 2025

  1. Merge pull request #5 from artempavlov/master

    Allow plain HTTP, ask user for POST_NOTIFICATIONS permission
    scottmconway authored Jan 6, 2025
    Copy the full SHA
    3c61855 View commit details
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -3,15 +3,28 @@ Notifikator

This is an Android application that catches every notification and forwards them to an HTTP endpoint.

It natively supports talking to the [Kodi](https://kodi.tv/) JSON-RPC interface, [Notifications for Android TV](https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google), as well as sending plain JSON.
It natively supports talking to the [Kodi](https://kodi.tv/) JSON-RPC interface, [Notifications for Android TV](https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google), [Gotify](https://gotify.net/), as well as sending plain JSON.

How to Use
----------

1. Install the application.
2. Enable notification access (a shortcut is provided within).
3. Configure the HTTP endpoint, protocol and authentication if necessary (see next section).
4. *(Optional)* Send a test notification.
4. *(Optional)* Configure the package denylist to silence noisy packages
5. *(Optional)* Send a test notification.

Package Denylist
---------
In order to deny specific applications from having their notifications forwarded, first get a list of your packages. This is simply obtained from running `adb shell pm list packages`. Applications' package names are also shown under the application's settings in the system settings application.

Format your package names in a text file with one package name per line. Remove the `package:` prefix if copy-pasting from the output of `pm list packages`. If generating this on a computer, `adb push` can be used to send this text file to your android device.

Click `Package Denylist File` in the app and select your denylist file.

Changes to the content of this file won't be automatically detected by the app - you must reload the file for any changes to take effect.

The packages specified in the denylist need not be installed on the device - they simply won't do anything if the given package never sends a notification.

Protocols
---------
@@ -40,6 +53,10 @@ This protocol forwards the notification to the server part of the [Notifications

The endpoint URL should look like `http://hostname:7676/`, and authentication should not be enabled.

### Gotify

This protocol simply POSTs a formatted JSON to the supplied URL. It expects a Gotify server's URL in this format: `https://gotify.example.com/message?token=$APP_TOKEN`. As of now, the `title` parameter is hard-coded to `Notifikator`, and `priority` to `5`.

### JSON

This protocol will simply `POST` a JSON containing the notification title, text, small and large icons to an arbitrary endpoint. This is designed for people who want to write their own server code to handle it.
@@ -57,4 +74,4 @@ Here is an example JSON, note that any of the fields not present in the original
"icon": "data:image/png;base64,..."
}
}
```
```
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ buildscript

dependencies
{
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.android.tools.build:gradle:8.0.1'
}
}

28 changes: 14 additions & 14 deletions client/build.gradle
Original file line number Diff line number Diff line change
@@ -2,18 +2,18 @@ apply plugin: 'com.android.application'

android
{
compileSdkVersion 27
buildToolsVersion "28.0.3"
defaultConfig
{
applicationId "net.kzxiv.notify.client"
minSdkVersion 21
targetSdkVersion 21
versionCode 2
versionName "1.1"
}
lintOptions
{
abortOnError false
}
compileSdkVersion 33
defaultConfig
{
applicationId "net.kzxiv.notify.client"
minSdkVersion 21
targetSdkVersion 33
versionCode 2
versionName "1.1"
}
lintOptions
{
abortOnError false
}
namespace 'net.kzxiv.notify.client'
}
61 changes: 34 additions & 27 deletions client/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.kzxiv.notify.client">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/icon"
android:theme="@style/AppTheme">
<service
android:name=".NotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<service
android:name=".HttpTransportService"
android:exported="false" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@drawable/icon"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<service
android:name=".NotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">

<activity android:name=".ConfigurationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>

<service
android:name=".HttpTransportService"
android:exported="false" />

<activity android:name=".ConfigurationActivity"
android:exported="true">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.kzxiv.notify.client;

public class AppConstants {
public static final String TAG = "notifikator";
public static final String PACKAGE_DENY_LIST_PREF_KEY = "package_deny_list";

}
106 changes: 53 additions & 53 deletions client/src/main/java/net/kzxiv/notify/client/BitmapHelper.java
Original file line number Diff line number Diff line change
@@ -8,63 +8,63 @@

final class BitmapHelper
{
public final static Bitmap getPackageIcon(Context context, String packageName, int id)
{
if (id == 0)
return null;
public final static Bitmap getPackageIcon(Context context, String packageName, int id)
{
if (id == 0)
return null;

try
{
Context remoteContext = context.createPackageContext(packageName, 0);
Drawable icon = remoteContext.getResources().getDrawable(id);
if (icon == null)
return null;
try
{
Context remoteContext = context.createPackageContext(packageName, 0);
Drawable icon = remoteContext.getResources().getDrawable(id);
if (icon == null)
return null;

return ((BitmapDrawable)icon).getBitmap();
}
catch (Exception ex)
{
return null;
}
}
return ((BitmapDrawable)icon).getBitmap();
}
catch (Exception ex)
{
return null;
}
}

public final static Bitmap ensureSize(Bitmap bitmap, int maxWidth, int maxHeight)
{
if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight)
return Bitmap.createScaledBitmap(bitmap, maxWidth, maxHeight, true);
else
return bitmap;
}
public final static Bitmap ensureSize(Bitmap bitmap, int maxWidth, int maxHeight)
{
if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight)
return Bitmap.createScaledBitmap(bitmap, maxWidth, maxHeight, true);
else
return bitmap;
}

public final static byte[] getBytes(Bitmap bitmap)
{
ByteArrayOutputStream strm = null;
try
{
strm = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, strm);
return strm.toByteArray();
}
finally
{
if (strm != null)
{
try
{
strm.close();
}
catch (IOException ex) {}
}
}
}
public final static byte[] getBytes(Bitmap bitmap)
{
ByteArrayOutputStream strm = null;
try
{
strm = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, strm);
return strm.toByteArray();
}
finally
{
if (strm != null)
{
try
{
strm.close();
}
catch (IOException ex) {}
}
}
}

public final static String getBase64(Bitmap bitmap)
{
return Base64.encodeToString(getBytes(bitmap), Base64.NO_WRAP);
}
public final static String getBase64(Bitmap bitmap)
{
return Base64.encodeToString(getBytes(bitmap), Base64.NO_WRAP);
}

public final static String getDataUri(Bitmap bitmap)
{
return "data:image/png;base64," + getBase64(bitmap);
}
public final static String getDataUri(Bitmap bitmap)
{
return "data:image/png;base64," + getBase64(bitmap);
}
}
Loading