Archive for Code

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

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

How to use an existing iOS project to start a new one (renaming) [XCode 8.1]

So after I spend a bunch of time trying to make it work.. here’s how for future reference and in case anyone else is struggling..

Read more

Android Studio and GIT: local file:// method. Creating a repo, push and pull on Windows.

Needing to work in a collaborative android project would require some version control and project sharing. Most of the time this happens within a local network at the office. Git offers various methods to do this, being using github the most common one, but if you feel like you don’t want to share your work with the wold, or you don’t want to be paying for a private membership, or simply the online thing is not the right approach, or you like instant pulling and pushing, or you don’t want a headache messing with SSH and windows services, you could do this within your already working network, skipping the need of any service setup or online service registration.

Read more

Using Ajax and Json with jQuery

Lets say your website page is already loaded but you need to poll your database for some extra info, or to check for an update on something. The way to go is using Ajax. jQuery makes working with Ajax really easy and even better if you return your data using Json.

Read more

Map ASAP with Ajax

 

Here is some short code to setup Google Maps on your site pretty fast. The map markers are grabbed through ajax using jquery from a php file. The example php provided contains an array of markets based on the ajax query. This, of course, should be changed with your own.

Read more

Easy way to cache your not so dynamic pages with PHP

So your website has now thousands of visitors? Well, congratulations… but your system processor is probably on a huge load if you haven’t added any caching options. Here is PHP to the rescue.

Read more