content: package com.afrihost.firebase.notifications;
  
  import android.app.Notification;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
  import android.content.Context;
  import android.content.Intent;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.graphics.Color;
  import android.net.Uri;
  import android.os.AsyncTask;
  import android.os.Build;
  import android.os.Bundle;
  import androidx.core.app.NotificationCompat;
  import androidx.core.app.RemoteInput;
  import android.util.Log;
  
  import com.facebook.react.bridge.Arguments;
  import com.facebook.react.bridge.Promise;
  import com.facebook.react.bridge.ReactApplicationContext;
  
  import java.io.IOException;
  import java.lang.ref.WeakReference;
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.List;
  
  import com.afrihost.firebase.notifications.Utils;
  
  public class DisplayNotificationTask extends AsyncTask<Void, Void, Void> {
    private static final String TAG = "DisplayNotificationTask";
    private final WeakReference<Context> contextWeakReference;
    private final WeakReference<ReactApplicationContext> reactContextWeakReference;
  
    private final Promise promise;
    private final Bundle notification;
    private final NotificationManager notificationManager;
  
    DisplayNotificationTask(
      Context context, ReactApplicationContext reactContext,
      NotificationManager notificationManager,
      Bundle notification, Promise promise
    ) {
      this.contextWeakReference = new WeakReference<>(context);
      this.reactContextWeakReference = new WeakReference<>(reactContext);
  
      this.promise = promise;
      this.notification = notification;
      this.notificationManager = notificationManager;
    }
  
    @Override
    protected void onPostExecute(Void result) {
      contextWeakReference.clear();
      reactContextWeakReference.clear();
    }
  
    @Override
    protected Void doInBackground(Void... voids) {
      Context context = contextWeakReference.get();
      if (context == null) return null;
  
      try {
        Class intentClass = getMainActivityClass(context);
  
        if (intentClass == null) {
          if (promise != null) {
            promise.reject(
              "notification/display_notification_error",
              "Could not find main activity class"
            );
          }
          return null;
        }
  
        Bundle android = notification.getBundle("android");
        String notificationId = notification.getString("notificationId");
  
        NotificationCompat.Builder nb;
        try {
          String channelId = android.getString("channelId");
          nb = new NotificationCompat.Builder(context, channelId);
        } catch (Throwable t) {
          // thrown if v4 android support library < 26
          nb = new NotificationCompat.Builder(context);
        }
  
        if (notification.containsKey("body")) {
          nb = nb.setContentText(notification.getString("body"));
        }
  
        if (notification.containsKey("data")) {
          nb = nb.setExtras(notification.getBundle("data"));
        }
  
        if (notification.containsKey("sound")) {
          Uri sound = RNFirebaseNotificationManager.getSound(
            context,
            notification.getString("sound")
          );
          nb = nb.setSound(sound);
        }
  
        if (notification.containsKey("subtitle")) {
          nb = nb.setSubText(notification.getString("subtitle"));
        }
  
        if (notification.containsKey("title")) {
          nb = nb.setContentTitle(notification.getString("title"));
        }
  
        if (android.containsKey("autoCancel")) {
          nb = nb.setAutoCancel(android.getBoolean("autoCancel"));
        }
  
        if (android.containsKey("badgeIconType") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          Double badgeIconType = android.getDouble("badgeIconType");
          try {
            nb = nb.setBadgeIconType(badgeIconType.intValue());
          } catch (Throwable t) {
            // thrown if v4 android support library < 26
            // do nothing
          }
        }
  
        if (android.containsKey("bigPicture")) {
          Bundle bigPicture = android.getBundle("bigPicture");
          NotificationCompat.BigPictureStyle bp = new NotificationCompat.BigPictureStyle();
          Bitmap picture = getBitmap(bigPicture.getString("picture"));
  
          if (picture != null) {
            bp = bp.bigPicture(picture);
          }
  
          if (bigPicture.containsKey("largeIcon")) {
            String largeIconStr = bigPicture.getString("largeIcon");
            if (largeIconStr == null) {
              bp = bp.bigLargeIcon(null);
            } else {
              Bitmap largeIconBitmap = getBitmap(largeIconStr);
              if (largeIconBitmap != null) {
                bp = bp.bigLargeIcon(largeIconBitmap);
              }
            }
          }
  
          if (bigPicture.containsKey("contentTitle")) {
            bp = bp.setBigContentTitle(bigPicture.getString("contentTitle"));
          }
  
          if (bigPicture.containsKey("summaryText")) {
            bp = bp.setSummaryText(bigPicture.getString("summaryText"));
          }
  
          nb = nb.setStyle(bp);
        }
  
        if (android.containsKey("bigText")) {
          Bundle bigText = android.getBundle("bigText");
  
          NotificationCompat.BigTextStyle bt = new NotificationCompat.BigTextStyle();
          bt.bigText(bigText.getString("text"));
          if (bigText.containsKey("contentTitle")) {
            bt = bt.setBigContentTitle(bigText.getString("contentTitle"));
          }
          if (bigText.containsKey("summaryText")) {
            bt = bt.setSummaryText(bigText.getString("summaryText"));
          }
          nb = nb.setStyle(bt);
        }
  
        if (android.containsKey("category")) {
          nb = nb.setCategory(android.getString("category"));
        }
  
        if (android.containsKey("color")) {
          String color = android.getString("color");
          nb = nb.setColor(Color.parseColor(color));
        }
  
        if (android.containsKey("colorized") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          try {
            nb = nb.setColorized(android.getBoolean("colorized"));
          } catch (Throwable t) {
            // thrown if v4 android support library < 26
            // do nothing
          }
        }
  
        if (android.containsKey("contentInfo")) {
          nb = nb.setContentInfo(android.getString("contentInfo"));
        }
  
        if (android.containsKey("defaults")) {
          Double defaultValues = android.getDouble("defaults");
          int defaults = defaultValues.intValue();
  
          if (defaults == 0) {
            ArrayList<Integer> defaultsArray = android.getIntegerArrayList("defaults");
            if (defaultsArray != null) {
              for (Integer defaultValue : defaultsArray) {
                defaults |= defaultValue;
              }
            }
          }
  
          nb = nb.setDefaults(defaults);
        }
  
        if (android.containsKey("group")) {
          nb = nb.setGroup(android.getString("group"));
        }
  
        if (android.containsKey("groupAlertBehaviour") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          Double groupAlertBehaviour = android.getDouble("groupAlertBehaviour");
          try {
            nb = nb.setGroupAlertBehavior(groupAlertBehaviour.intValue());
          } catch (Throwable t) {
            // thrown if v4 android support library < 26
            // do nothing
          }
        }
  
        if (android.containsKey("groupSummary")) {
          nb = nb.setGroupSummary(android.getBoolean("groupSummary"));
        }
  
        if (android.containsKey("largeIcon")) {
          Bitmap largeIcon = getBitmap(android.getString("largeIcon"));
          if (largeIcon != null) {
            nb = nb.setLargeIcon(largeIcon);
          }
        }
  
        if (android.containsKey("lights")) {
          Bundle lights = android.getBundle("lights");
          Double argb = lights.getDouble("argb");
          Double onMs = lights.getDouble("onMs");
          Double offMs = lights.getDouble("offMs");
          nb = nb.setLights(argb.intValue(), onMs.intValue(), offMs.intValue());
        }
  
        if (android.containsKey("localOnly")) {
          nb = nb.setLocalOnly(android.getBoolean("localOnly"));
        }
  
        if (android.containsKey("number")) {
          Double number = android.getDouble("number");
          nb = nb.setNumber(number.intValue());
        }
  
        if (android.containsKey("ongoing")) {
          nb = nb.setOngoing(android.getBoolean("ongoing"));
        }
  
        if (android.containsKey("onlyAlertOnce")) {
          nb = nb.setOnlyAlertOnce(android.getBoolean("onlyAlertOnce"));
        }
  
        if (android.containsKey("people")) {
          List<String> people = android.getStringArrayList("people");
          if (people != null) {
            for (String person : people) {
              nb = nb.addPerson(person);
            }
          }
        }
  
        if (android.containsKey("priority")) {
          Double priority = android.getDouble("priority");
          nb = nb.setPriority(priority.intValue());
        }
  
        if (android.containsKey("progress")) {
          Bundle progress = android.getBundle("progress");
          Double max = progress.getDouble("max");
          Double progressI = progress.getDouble("progress");
          nb = nb.setProgress(
            max.intValue(),
            progressI.intValue(),
            progress.getBoolean("indeterminate")
          );
        }
  
        // TODO: Public version of notification
        /* if (android.containsKey("publicVersion")) {
          nb = nb.setPublicVersion();
        } */
  
        if (android.containsKey("remoteInputHistory")) {
          nb = nb.setRemoteInputHistory(android.getStringArray("remoteInputHistory"));
        }
  
        if (android.containsKey("shortcutId") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          try {
            nb = nb.setShortcutId(android.getString("shortcutId"));
          } catch (Throwable t) {
            // thrown if v4 android support library < 26
            // do nothing
          }
        }
  
        if (android.containsKey("showWhen")) {
          nb = nb.setShowWhen(android.getBoolean("showWhen"));
        }
  
        if (android.containsKey("smallIcon")) {
          Bundle smallIcon = android.getBundle("smallIcon");
          int smallIconResourceId = getIcon(smallIcon.getString("icon"));
          if (smallIconResourceId != 0) {
            if (smallIcon.containsKey("level")) {
              Double level = smallIcon.getDouble("level");
              nb = nb.setSmallIcon(smallIconResourceId, level.intValue());
            } else {
              nb = nb.setSmallIcon(smallIconResourceId);
            }
          }
        }
  
        if (android.containsKey("sortKey")) {
          nb = nb.setSortKey(android.getString("sortKey"));
        }
  
        if (android.containsKey("ticker")) {
          nb = nb.setTicker(android.getString("ticker"));
        }
  
        if (android.containsKey("timeoutAfter")) {
          Double timeoutAfter = android.getDouble("timeoutAfter");
          nb = nb.setTimeoutAfter(timeoutAfter.longValue());
        }
  
        if (android.containsKey("usesChronometer")) {
          nb = nb.setUsesChronometer(android.getBoolean("usesChronometer"));
        }
  
        if (android.containsKey("vibrate")) {
          ArrayList<Integer> vibrate = android.getIntegerArrayList("vibrate");
          if (vibrate != null) {
            long[] vibrateArray = new long[vibrate.size()];
            for (int i = 0; i < vibrate.size(); i++) {
              vibrateArray[i] = vibrate
                .get(i)
                .longValue();
            }
            nb = nb.setVibrate(vibrateArray);
          }
        }
  
        if (android.containsKey("visibility")) {
          Double visibility = android.getDouble("visibility");
          nb = nb.setVisibility(visibility.intValue());
        }
  
        if (android.containsKey("when")) {
          Double when = android.getDouble("when");
          nb = nb.setWhen(when.longValue());
        }
  
        // Build any actions
        if (android.containsKey("actions")) {
          List<Bundle> actions = (List) android.getSerializable("actions");
          for (Bundle a : actions) {
            NotificationCompat.Action action = createAction(context, a, intentClass, notification);
            nb = nb.addAction(action);
          }
        }
  
        String tag = null;
        if (android.containsKey("tag")) {
          tag = android.getString("tag");
        }
  
        // Create the notification intent
        PendingIntent contentIntent = createIntent(
          context,
          intentClass,
          notification,
          android.getString("clickAction")
        );
  
        nb = nb.setContentIntent(contentIntent);
  
        // Build the notification and send it
        Notification builtNotification = nb.build();
        notificationManager.notify(tag, notificationId.hashCode(), builtNotification);
  
        if (reactContextWeakReference.get() != null) {
          Utils.sendEvent(
            reactContextWeakReference.get(),
            "notifications_notification_displayed",
            Arguments.fromBundle(notification)
          );
        }
  
        if (promise != null) {
          promise.resolve(null);
        }
  
      } catch (Exception e) {
        Log.e(TAG, "Failed to send notification", e);
        if (promise != null) {
          promise.reject("notification/display_notification_error", "Could not send notification", e);
        }
      }
  
      return null;
    }
  
    private NotificationCompat.Action createAction(
      Context context,
      Bundle action,
      Class intentClass,
      Bundle notification
    ) {
      String actionKey = action.getString("action");
      boolean showUserInterface = action.containsKey("showUserInterface") && action.getBoolean(
        "showUserInterface");
  
      PendingIntent actionIntent = showUserInterface ?
        createIntent(context, intentClass, notification, actionKey) :
        createBroadcastIntent(context, notification, actionKey);
  
      int icon = getIcon(action.getString("icon"));
      String title = action.getString("title");
  
      NotificationCompat.Action.Builder ab = new NotificationCompat.Action.Builder(
        icon,
        title,
        actionIntent
      );
  
      if (action.containsKey("allowGeneratedReplies")) {
        ab = ab.setAllowGeneratedReplies(action.getBoolean("allowGeneratedReplies"));
      }
  
      if (action.containsKey("remoteInputs")) {
        List<Bundle> remoteInputs = (List) action.getSerializable("remoteInputs");
        for (Bundle ri : remoteInputs) {
          RemoteInput remoteInput = createRemoteInput(ri);
          ab = ab.addRemoteInput(remoteInput);
        }
      }
  
      // TODO: SemanticAction and ShowsUserInterface only available on v28?
      // if (action.containsKey("semanticAction")) {
      //   Double semanticAction = action.getDouble("semanticAction");
      //   ab = ab.setSemanticAction(semanticAction.intValue());
      // }
      // if (action.containsKey("showsUserInterface")) {
      //   ab = ab.setShowsUserInterface(action.getBoolean("showsUserInterface"));
      // }
  
      return ab.build();
    }
  
    private PendingIntent createIntent(
      Context context,
      Class intentClass,
      Bundle notification,
      String action
    ) {
      Intent intent = new Intent(context, intentClass);
      intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
      intent.putExtras(notification);
  
      if (action != null) {
        intent.setAction(action);
      }
  
      String notificationId = notification.getString("notificationId");
      return PendingIntent.getActivity(
        context,
        notificationId.hashCode(),
        intent,
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
                  PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE :
                  PendingIntent.FLAG_UPDATE_CURRENT
      );
    }
  
    private PendingIntent createBroadcastIntent(Context context, Bundle notification, String action) {
      String notificationId = notification.getString("notificationId") + action;
      Intent intent = new Intent(context, RNFirebaseBackgroundNotificationActionReceiver.class);
  
      intent.putExtra("action", action);
      intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
      intent.putExtra("notification", notification);
      intent.setAction("io.invertase.firebase.notifications.BackgroundAction");
  
      return PendingIntent.getBroadcast(
        context,
        notificationId.hashCode(),
        intent,
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
                  PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE :
                  PendingIntent.FLAG_UPDATE_CURRENT
      );
    }
  
    private RemoteInput createRemoteInput(Bundle remoteInput) {
      String resultKey = remoteInput.getString("resultKey");
      RemoteInput.Builder rb = new RemoteInput.Builder(resultKey);
  
      if (remoteInput.containsKey("allowedDataTypes")) {
        List<Bundle> allowedDataTypes = (List) remoteInput.getSerializable("allowedDataTypes");
        for (Bundle adt : allowedDataTypes) {
          rb.setAllowDataType(adt.getString("mimeType"), adt.getBoolean("allow"));
        }
      }
  
      if (remoteInput.containsKey("allowFreeFormInput")) {
        rb.setAllowFreeFormInput(remoteInput.getBoolean("allowFreeFormInput"));
      }
  
      if (remoteInput.containsKey("choices")) {
        List<String> choices = remoteInput.getStringArrayList("choices");
        rb.setChoices(choices.toArray(new String[choices.size()]));
      }
  
      if (remoteInput.containsKey("label")) {
        rb.setLabel(remoteInput.getString("label"));
      }
  
      return rb.build();
    }
  
    private Bitmap getBitmap(String image) {
      if (image.startsWith("http://") || image.startsWith("https://")) {
        return getBitmapFromUrl(image);
      }
  
      if (image.startsWith("file://")) {
        return BitmapFactory.decodeFile(image.replace("file://", ""));
      }
  
      int largeIconResId = getIcon(image);
      return BitmapFactory.decodeResource(
        contextWeakReference.get().getResources(),
        largeIconResId
      );
    }
  
    private Bitmap getBitmapFromUrl(String imageUrl) {
      try {
        HttpURLConnection connection = (HttpURLConnection) new URL(imageUrl).openConnection();
        connection.setDoInput(true);
        connection.connect();
        return BitmapFactory.decodeStream(connection.getInputStream());
      } catch (IOException e) {
        Log.e(TAG, "Failed to get bitmap for url: " + imageUrl, e);
        return null;
      }
    }
  
    private int getIcon(String icon) {
      int resourceId = RNFirebaseNotificationManager.getResourceId(
        contextWeakReference.get(),
        "mipmap",
        icon
      );
  
      if (resourceId == 0) {
        resourceId = RNFirebaseNotificationManager.getResourceId(
          contextWeakReference.get(),
          "drawable",
          icon
        );
      }
  
      return resourceId;
    }
  
    private Class getMainActivityClass(Context context) {
      String packageName = context.getPackageName();
      Intent launchIntent = context
        .getPackageManager()
        .getLaunchIntentForPackage(packageName);
  
      try {
        return Class.forName(launchIntent.getComponent().getClassName());
      } catch (ClassNotFoundException e) {
        Log.e(TAG, "Failed to get main activity class", e);
        return null;
      }
    }
  }
  