Skip to content

Commit

Permalink
Merge pull request #12 from viktorholk/1.2.1
Browse files Browse the repository at this point in the history
1.2.1
  • Loading branch information
viktorholk authored Aug 29, 2024
2 parents bda497e + e69ae6e commit 333c952
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 37 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
.idea/
51 changes: 40 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,54 @@ The port can be changed in the ``server/.env`` file
## Create a Notification
> ### Example using [curl](https://curl.se/)
> ````
> curl '127.0.0.1:5000' \
> curl '127.0.0.1:3000' \
> --header 'Content-Type: application/json' \
> --data '{
> "title": "Foo Bar Baz!",
> "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
> "message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
> "url": "http://example.com",
> }'
> ````
>|Property|Type|Description|Required|
>|---|---|---|---|
>|title|String|The title of the notification|**Yes**|
>|message|String|The longer text that will be included in the notification|No|
>|url|String|Open the URL on notifcation press|No|
>
> #### Response
> `Created 201`
## Get All Notifications
>
> ````
> curl '127.0.0.1:3000'
> ````
> #### Response
> ````
>[
> {
> "title": "Foo Bar Baz!",
> "message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
> "url": "http://example.com",
> },
> ...
>]
> ````
## Get Latest Notifications
>
> ````
> curl '127.0.0.1:3000/latest'
> ````
> #### Response
> ````
>{
> "status": 201,
> "message": "Successfully created notification",
> "data": {
> "title": "Foo Bar Baz!",
> "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
> }
>}
> {
> "title": "Foo Bar Baz!",
> "message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
> "url": "http://example.com",
> }
> ````
>
## Connect to the server stream
In the configuration tab type in the `/events` endpoint on your server
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "com.viktorholk.apipushnotifications"
minSdk 26
targetSdk 35
versionCode 7
versionName "1.2.0"
versionCode 8
versionName "1.2.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import java.util.regex.Pattern;

public class ConfigurationFragment extends Fragment {

private static final Pattern URL_PATTERN = Pattern.compile("https?:\\/\\/(.*)");
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;

Expand Down Expand Up @@ -48,9 +46,7 @@ private void applyConfiguration(String urlText) {
if (urlText.length() == 0)
return;

if (!URL_PATTERN.matcher(urlText).matches()) {
urlText = "http://" + urlText;
}
urlText = Utils.parseURL(urlText);

String urlShared = sharedPreferences.getString("url", "");
if (!urlText.equals(urlShared)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
Expand Down Expand Up @@ -41,6 +43,11 @@ public class NotificationsService extends Service {
private OkHttpClient client;
private Call currentCall;
private boolean isStoppedByUser = false;

private static final int MAX_RETRIES = 5;
private static final int RETRY_TIME = 2000;
private int retryCount = 0;

private final Intent serviceFragmentBroadcast = new Intent("serviceFragmentBroadcast");

@Override
Expand All @@ -64,6 +71,8 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}

private void listenForNotifications() {
broadcast("Connecting...", false);

String url = MainActivity.sharedPreferences.getString("url", "");
Request request = new Request.Builder()
.addHeader("Accept", "text/event-stream")
Expand All @@ -74,18 +83,19 @@ private void listenForNotifications() {
currentCall.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
handleFailure(e);
handleFailure(e, true);
}

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
retryCount = 0;
if (!response.isSuccessful()) {
handleFailure(new IOException("Response failed with status code: " + response.code()));
handleFailure(new IOException("Response failed with status code: " + response.code()), false);
return;
}

if (!"text/event-stream".equals(response.header("Content-Type"))) {
handleFailure(new IOException("Expected response content type to be an event stream"));
handleFailure(new IOException("Expected response content type to be an event stream"), false);
return;
}

Expand All @@ -94,10 +104,27 @@ public void onResponse(@NonNull Call call, @NonNull Response response) throws IO
});
}

private void handleFailure(IOException e) {
Log.w(LOG_TAG, "Failure: " + e);
broadcast(e.toString(), true);
stopSelf();

private void handleFailure(IOException e, boolean withRetry) {
if (isStoppedByUser) {
broadcast("Stopped", false);
return;
}

// Try to reconnect
if (withRetry && (retryCount < MAX_RETRIES)) {
retryCount++;
broadcast(String.format("Retrying Connection (%s) \n%s", retryCount, e), false);
try {
Thread.sleep(RETRY_TIME);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
}
listenForNotifications();
} else {
broadcast(e.toString(), true);
stopSelf();
}
}

private void handleSuccess(@NonNull Response response) throws IOException {
Expand All @@ -117,11 +144,10 @@ private void handleSuccess(@NonNull Response response) throws IOException {
}
}
} catch (Exception e) {
if (isStoppedByUser)
if (isStoppedByUser) {
broadcast("Stopped", false);
else
handleFailure(new IOException("Lost connection"));

} else
handleFailure(new IOException("Lost connection"), true);
break;
}
}
Expand Down Expand Up @@ -155,7 +181,6 @@ private void createNotificationChannels() {

private void startForegroundService() {
Notification notification = new NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID)
.setOngoing(true)
.setCategory(Notification.CATEGORY_SERVICE)
.build();

Expand All @@ -176,6 +201,23 @@ private void showNotification(PushNotification notification) {
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);

String notificationUrl = notification.getUrl();
if (notificationUrl != null && !notificationUrl.isEmpty()) {

notificationUrl = Utils.parseURL(notificationUrl);

// Create the intent and pending intent
Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(notificationUrl));
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE
);

builder.setContentIntent(pendingIntent);
}

NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (notificationManager != null) {
Log.i(LOG_TAG, "Notifying: " + notification.getTitle());
Expand All @@ -187,7 +229,7 @@ private void broadcast(String message, boolean isError) {
if (!Objects.isNull(message)) {
serviceFragmentBroadcast.putExtra("message", message);
serviceFragmentBroadcast.putExtra("isError", isError);
Log.i("Notification Service Broadcast", message);
Log.i(LOG_TAG, message);
}
sendBroadcast(serviceFragmentBroadcast);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ public class PushNotification {

private String title;
private String message;
private String url;

public PushNotification(String title, String message) {
public PushNotification(String title, String message, String url) {
this.title = title;
this.message = message;
this.url = url;
}

public String getTitle() {
Expand All @@ -24,4 +26,8 @@ public String getMessage() {
return message;
}

public String getUrl() {
return url;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ private void toggleService() {
} else {
// Clear the previous error message
serviceMessageTextView.setText("");
Log.e("test", Boolean.toString(NotificationsService.running));

// Start the service
getActivity().startService(new Intent(getActivity(), NotificationsService.class));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.viktorholk.apipushnotifications;

import java.util.regex.Pattern;

public class Utils {
public static String parseURL(String value) {
final Pattern pattern = Pattern.compile("https?:\\/\\/(.*)");

// Add http to the URL if no protocol is defined
if (!pattern.matcher(value).matches()) {
value = String.format("http://%s", value);
}

return value;
}


}
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.0'
classpath 'com.android.tools.build:gradle:8.1.4'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Push Notifications API Server",
"version": "1.2.0",
"version": "1.2.1",
"description": "Server for handling push notifications",
"scripts": {
"start": "ts-node --files -r tsconfig-paths/register src/index.ts"
Expand Down
6 changes: 4 additions & 2 deletions server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const router = Router();
interface PushNotification {
title: string;
message?: string;
url?: string
}

type ClientContext = { req: Request; res: Response };
Expand All @@ -23,13 +24,14 @@ function sendNotifications(notification: PushNotification): void {

// Create new notification
router.post("/", (req: Request, res: Response) => {
const { title, message } = req.body;

const { title, message, url } = req.body;

if (!title || title.trim() === "") {
return res.status(400).send("'title' field is required");
}

const notification: PushNotification = { title, message };
const notification: PushNotification = { title, message, url };

notifications.push(notification);
sendNotifications(notification);
Expand Down

0 comments on commit 333c952

Please sign in to comment.