content: package com.alvinhkh.buseta.service;
  
  import android.app.IntentService;
  import android.app.NotificationChannel;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
  import android.content.Context;
  import android.content.Intent;
  import android.os.Build;
  import androidx.preference.PreferenceManager;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.core.app.NotificationManagerCompat;
  import androidx.core.app.TaskStackBuilder;
  import androidx.core.content.ContextCompat;
  import androidx.core.app.NotificationCompat;
  
  import com.alvinhkh.buseta.C;
  import com.alvinhkh.buseta.R;
  import com.alvinhkh.buseta.search.ui.SearchActivity;
  import com.google.android.gms.location.Geofence;
  import com.google.android.gms.location.GeofencingEvent;
  
  import java.util.List;
  
  import timber.log.Timber;
  
  /**
   * Listener for geofence transition changes.
   *
   * Receives geofence transition events from Location Services in the form of an Intent containing
   * the transition type and geofence id(s) that triggered the transition. Creates a notification
   * as the output.
   */
  public class GeofenceTransitionsIntentService extends IntentService {
  
      /**
       * This constructor is required, and calls the super IntentService(String)
       * constructor with the name for a worker thread.
       */
      public GeofenceTransitionsIntentService() {
          super("GeofenceTransitionsIS");
      }
  
      /**
       * Handles incoming intents.
       * @param intent sent by Location Services. This Intent is provided to Location
       *               Services (inside a PendingIntent) when addGeofences() is called.
       */
      @Override
      protected void onHandleIntent(Intent intent) {
          GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
          if (geofencingEvent.hasError()) {
              Timber.e("GeofenceError: %s", geofencingEvent.getErrorCode());
              return;
          }
          int transition = geofencingEvent.getGeofenceTransition();
          if (transition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                  transition == Geofence.GEOFENCE_TRANSITION_EXIT) {
  
              List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
  
              for (Geofence geofence: triggeringGeofences) {
                  sendNotification(geofence.getRequestId().hashCode(),
                          getTransitionString(transition), geofence.getRequestId());
              }
  
              // remove current geofences key
              PreferenceManager.getDefaultSharedPreferences(this)
                      .edit()
                      .putString(C.PREF.GEOFENCES_KEY, "")
                      .apply();
          } else {
              Timber.d("GeofenceError: %s", transition);
          }
      }
  
      private void sendNotification(int id, @NonNull String title, @Nullable String text) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
              NotificationManager notificationManager =
                      (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
              if (notificationManager != null) {
                  NotificationChannel channel = new NotificationChannel(C.NOTIFICATION.CHANNEL_ARRIVAL_ALERT,
                          getString(R.string.channel_name_arrival_alert), NotificationManager.IMPORTANCE_HIGH);
                  channel.setDescription(getString(R.string.channel_description_arrival_alert));
                  channel.enableLights(true);
                  channel.enableVibration(true);
                  notificationManager.createNotificationChannel(channel);
              }
          }
  
          // Create an explicit content Intent that starts the main Activity.
          Intent notificationIntent = new Intent(getApplicationContext(), SearchActivity.class);
  
          // Construct a task stack.
          TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
  
          // Add the main Activity to the task stack as the parent.
          stackBuilder.addParentStack(SearchActivity.class);
  
          // Push the content Intent onto the stack.
          stackBuilder.addNextIntent(notificationIntent);
  
          // Get a PendingIntent containing the entire back stack.
          PendingIntent notificationPendingIntent =
                  stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
  
          NotificationCompat.Builder builder = new NotificationCompat.Builder(this, C.NOTIFICATION.CHANNEL_ARRIVAL_ALERT);
          builder.setSmallIcon(R.drawable.ic_outline_directions_bus_24dp)
                  //.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_directions_bus_white_24dp))
                  .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
                  .setContentTitle(title)
                  .setContentText(text)
                  .setContentIntent(notificationPendingIntent);
          builder.setAutoCancel(true);
  
          NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
          notificationManager.notify(id, builder.build());
      }
  
      /**
       * Maps geofence transition types to their human-readable equivalents.
       *
       * @param transitionType    A transition type constant defined in Geofence
       * @return                  A String indicating the type of transition
       */
      private String getTransitionString(int transitionType) {
          switch (transitionType) {
              case Geofence.GEOFENCE_TRANSITION_ENTER:
                  return getString(R.string.arrival_alert_entered);
              case Geofence.GEOFENCE_TRANSITION_EXIT:
                  return getString(R.string.arrival_alert_leaving);
              default:
                  return getString(R.string.arrival_alert_unknown);
          }
      }
  }
  