Sending push notifications to Firebase from a java server without the bizillion Google dependencies

If you’re strugling to send Android push notifications from your server, specially with the bizillion libraries, dependencies, graddle nuances and error rabbit holes.. know you’re not alone, and also be aware that there is another way to do this without all that library hell.

This method will grab Google credentials and then push notifications to Firebase from a java server. Sure, it still uses some libraries, but all these can be grabbed as simple jars from Maven, or the traditionall Gradle implementation way, but they wont, in turn, download a bunch of incompatible dependency libs. Also, it will be a lot easier to adapt this code to any other language like Python, PHP, etc .. specially if you ask AI.

You should already have your project im firebase and able to receive push noficitions from the firebase messaging website, also, you need to have your service-account-key.json file in hand.

Please keep in mind this is a very raw code, you’ll need to modify it to access your credentials and to send messages to multiple receivers.

 

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.lang.JoseException;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;

public class NotificationManager {

    //these come from the serviceaccountkey.json
    final String project_id = "*****";
    final String client_email = "firebase-adminsdk-fbsvc@*****.iam.gserviceaccount.com";
    final String private_key_id = "******";
    //please remove -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- from private_key
    final String private_key = "*****"; 

    String cachedAccessToken = "";
    long tokenExpirationTime = 0;
    private static final String TOKEN_URI = "https://oauth2.googleapis.com/token";
    
    public NotificationManager(){
        System.out.println("NotificationManager created");
    }

    public sendPushNotification(String title, String message, String extra, String deviceToken){
        //you more likely send this via params
        title = "New message";
        message = "hello world";
        extra = "extradata";
        deviceToken = "****"; //this token arrives to each Android device on app launch. Pass it to the server

        //lets not block the main thread
        new Thread(new sendPushNotificationRunnable(title, message, extra, deviceToken)).start();
    }

    class sendPushNotificationRunnable implements Runnable{
        String title, msg, extra, deviceToken;

        public sendPushNotificationRunnable(String title, String msg, String extra, String deviceToken) {
            this.title = title;
            this.msg = msg;
            this.extra = extra;
            this.deviceToken = deviceToken;
        }

        public void run() throws RuntimeException {
            String accessToken = getAccessToken();
            sendNotification(accessToken);
        }

        public String getAccessToken(){
            //here we will ask Google Cloud for Firebase credentials, and return them as a String        

            String newAccessToken = cachedAccessToken;
            long currentTimeSeconds = System.currentTimeMillis() / 1000;

            //if last token is still valid, we will skip all this and return the cached one
            if (cachedAccessToken.equals("") || currentTimeSeconds > tokenExpirationTime - 300) {

                JwtClaims claims = new JwtClaims();
                claims.setIssuer(client_email);
                claims.setSubject(client_email);
                claims.setAudience(TOKEN_URI);
                claims.setIssuedAtToNow();
                claims.setExpirationTimeMinutesInTheFuture(60);
                claims.setStringClaim("scope", "https://www.googleapis.com/auth/firebase.messaging");

                JsonWebSignature jws = new JsonWebSignature();
                jws.setPayload(claims.toJson());
                jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
                jws.setHeader("kid", private_key_id);

                String pemContent = private_key.replaceAll("\\s", "");
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(pemContent));

                String jwsKey = "";
                try {
                    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                    jws.setKey(keyFactory.generatePrivate(keySpec));
                    jwsKey = jws.getCompactSerialization();
                } catch (NoSuchAlgorithmException | InvalidKeySpecException | JoseException e) {
                    throw new RuntimeException(e);
                }


                if(!jwsKey.equals("")){
                    HttpURLConnection connection = null;

                    try {
                        URL url = new URL(TOKEN_URI);
                        String requestBody = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + jwsKey;
                        byte[] postData = requestBody.getBytes(StandardCharsets.UTF_8);

                        connection = (HttpURLConnection) url.openConnection();
                        connection.setRequestMethod("POST");
                        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                        connection.setDoOutput(true);
                        connection.setRequestProperty("Content-Length", String.valueOf(postData.length));

                        System.out.println("Sending token exchange request to " + TOKEN_URI + "...");

                        // Write the request body
                        try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
                            wr.write(postData);
                            wr.flush();
                        }

                        int responseCode = connection.getResponseCode();
                        StringBuilder response = new StringBuilder();

                        // Read the response
                        try (BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                        (responseCode == HttpURLConnection.HTTP_OK) ? connection.getInputStream() : connection.getErrorStream(),
                                        StandardCharsets.UTF_8))) {
                            String inputLine;
                            while ((inputLine = in.readLine()) != null) {
                                response.append(inputLine);
                            }
                        }

                        String responseBody = response.toString();
                        if (responseCode == HttpURLConnection.HTTP_OK) {
                            Gson gson = new Gson();
                            Type typeOfHashMap = new TypeToken<Map<String, Object>>() {}.getType();
                            Map<String, Object> tokenResponseMap;
                            try {
                                tokenResponseMap = gson.fromJson(responseBody, typeOfHashMap);
                            } catch (JsonSyntaxException e) {
                                throw new IOException("Failed to parse token response JSON: " + e.getMessage() + "\nResponse: " + responseBody, e);
                            }

                            cachedAccessToken = (String) tokenResponseMap.get("access_token");
                            long expiresIn = ((Double)tokenResponseMap.get("expires_in")).longValue();
                            tokenExpirationTime = (System.currentTimeMillis() / 1000) + expiresIn;
                            System.out.println("Access Token obtained successfully. Expires in " + expiresIn + " seconds.");
                            newAccessToken = cachedAccessToken;
                        } else {
                            throw new IOException("Failed to get access token. Status: " + responseCode + ", Response: " + responseBody);
                        }
                    } catch (IOException e) {
                        System.out.println("Token error: " + e.getMessage());
                    } finally {
                        if (connection != null) {
                            connection.disconnect();
                        }
                    }
                }
            }
            return newAccessToken;
        }

        public void sendNotification(String accessToken){
            //Here we will finally access Firebase using the Google Access token, 
            //and submit a payload with the notification

            if (!FirebaseToken.equals("")) {
                try {
                    URL url = new URL("https://fcm.googleapis.com/v1/projects/"+project_id+"/messages:send");
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("POST");
                    connection.setRequestProperty("Content-Type", "application/json; UTF-8");
                    connection.setRequestProperty("Authorization", "Bearer " + accessToken);
                    connection.setUseCaches(false);
                    connection.setDoOutput(true);


                    if (!deviceToken.equals("")) {
                        String jsonPayload = (
                                "{'message': {"+
                                "'token':'"+deviceToken+"',"+
                                "'data':{'title':'"+title+"','body':'"+msg+"','extra':'"+extra+"},"+
                                "'android':{'priority':'HIGH'}}}"
                        ).replace("'","\"");

                        try (OutputStream os = connection.getOutputStream()) {
                            byte[] input = jsonPayload.getBytes(StandardCharsets.UTF_8);
                            os.write(input, 0, input.length);
                        }

                        int responseCode = connection.getResponseCode();
                        if (responseCode == HttpURLConnection.HTTP_OK) {
                            System.out.println("Notification was send");
                        } else {
                            String errorResponse = new String(connection.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
                            System.out.println("Notification error: " + errorResponse);
                        }
                    }

                    connection.disconnect();

                }catch(Exception e){
                    System.out.println("Notification error: " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }
}

 

 

 

Single click chrome extension to run JS on a website (manifest version 3)

If you know Javascript and want to be able to customize how a website looks every time you visit it, you can create a chrome extension and have it handy in your chrome bar. Some use cases could be to make the font bigger, remove elements, change apperiance, change how the website works, add stuff, disable stuff, etc.

Read more

Peaje Chongon Guayaquil – Salinas, Playas, Posorja, Engabao

En este post espero aclarar dudas de como funciona el telepeaje a de Chongon, Guayaquil a Salinas o Playas.

La empresa que administra el telepeaje se llama Cvialco y la plataforma para el manejo de tus stickers TeleTags y tu saldo es https://miteletag.com.ec

 

 

Read more

Use Autovoice to send commands from a Google home device to tasker

In this guide ill show you how to send commands from any Google Home device or assistant to tasker. This works from a Google Home device like the mini, from one phone to another, or with the assistant in the same phone.

Read more

Send commands from Google Home to tasker using IFTTT

Part 1, Setting up IFTTT

In order to hook up Google Home assistant with Tasker, we need to be able to receive push notifications by using the IFTTT app to trigger Tasker profiles.

Read more

Control termux remotely from a computer

This guide is intended to be included on other articles as an alternative to typing in all those commands on a termux terminal, since Android keyboards are not the most confortable and complete with all the symbols and characters required. Make sure that your Android device and your computer are both connected to the same router.

Read more

Guide to control a Tuya Smart device from Tasker (no root needed)

Tuya smart devices are cheap, easy to get and they just work. A lot of different brands follow their protocols and design their own apps that look a lot like Tuya’s own application.

One of theese many apps, that I personally use is Smart Life. Tuya smart apps keep a lot of security under their hoods, but some older versions are a bit less secure.

Read more

Android notifications from a Java server

With the following script, youll be able to send push notifications to Android devices from a Java server, without implementing any of the Firebase frameworks to keep your server tidy and less cluttered.

Read more

Guide to control a PlayStation 4 from Google Home using an Android TV or old phone as a hub (no root needed)

This guide will walk you through to the steps to setup an Android device to be able to invoke ps4-waker through tasker in a rooted or non rooted device.

PS4-Walker is a NodeJS program developed by Daniel Leong that allows you to command your PS4 from another device.

It is probably easier to setup a Respberry Pi since it can run Linux natively (there are guides out there of how-to), but this guide will show you to work it out without a Raspberry, by giving a new use to an old spare Android device or with your current Android TV box, plus, how to setup programs to hook it up with your Google Home or Google Assistant (and probably to Alexa aswell).

It takes several steps so ill try to be as clear as possible how this can be done. At the end, youll be able to “Ok Google, turn ps4 on” or “Ok Google, start Youtube”.

Read more

SMS Money Widget (Policy)

SMS Money Widget checks the incoming SMS from your bank or credit card issuer in order update a widget in your home screen with your current expenses in a monthly basis. Please allow the app to read your SMS and add the widget to your home screen for this purpose.

SMS Money Widget will not post, share or trade your personal information, SMSs or balance status in any way. The only time the app will send information over the internet will be for crash reports in order to fix bugs. Crash reports contain no personal information.

 

Get it on Google Play