content: /*
   * Copyright (C) 2016 Actinarium
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.actinarium.rhythm.control;
  
  import android.app.Application;
  import android.app.IntentService;
  import android.app.Notification;
  import android.app.NotificationManager;
  import android.app.PendingIntent;
  import android.content.Context;
  import android.content.Intent;
  import android.os.Handler;
  import android.os.Looper;
  import android.support.v4.app.NotificationCompat;
  import com.actinarium.rhythm.RhythmOverlay;
  
  
  /**
   * An intent service backing Quick Control notification functionality. Requires {@link Application} to implement {@link
   * RhythmControl.Host} to access the application’s {@link RhythmControl} singleton instance
   *
   * @author Paul Danyliuk
   */
  public class RhythmNotificationService extends IntentService {
  
      static final String ACTION_SHOW_QUICK_CONTROL = "com.actinarium.rhythm.action.SHOW_QUICK_CONTROL";
      static final String ACTION_NEXT_GROUP = "com.actinarium.rhythm.action.NEXT_GROUP";
      static final String ACTION_NEXT_OVERLAY = "com.actinarium.rhythm.action.NEXT_OVERLAY";
      static final String ACTION_DISMISS_QUICK_CONTROL = "com.actinarium.rhythm.action.DISMISS_QUICK_CONTROL";
  
      static final String EXTRA_NOTIFICATION_ID = "com.actinarium.rhythm.extra.NOTIFICATION_ID";
  
      private static final int NOTIFICATION_ICON_COLOR = 0x105AAE;
      private static final int NOTIFICATION_ERROR_COLOR = 0xEF4343;
  
      public RhythmNotificationService() {
          super("RhythmService");
      }
  
      /**
       * Show the &ldquo;Quick Control&rdquo; notification, which allows to switch overlays for all connected Rhythm
       * groups without leaving the app under development. Can be used to update the notification as well.
       *
       * @param context        to start the service
       * @param notificationId Notification ID, must be unique across the app
       */
      static void showNotification(Context context, int notificationId) {
          Intent intent = new Intent(context, RhythmNotificationService.class);
          intent.setAction(ACTION_SHOW_QUICK_CONTROL);
          intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
          context.startService(intent);
      }
  
      @Override
      protected void onHandleIntent(Intent intent) {
          if (intent != null) {
              final String action = intent.getAction();
              if (ACTION_SHOW_QUICK_CONTROL.equals(action)) {
                  final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, Integer.MIN_VALUE);
                  handleShowNotification(notificationId);
              } else if (ACTION_NEXT_OVERLAY.equals(action)) {
                  handleNextOverlay();
              } else if (ACTION_NEXT_GROUP.equals(action)) {
                  handleNextGroup();
              } else if (ACTION_DISMISS_QUICK_CONTROL.equals(action)) {
                  handleDismissQuickConfig();
              }
          }
      }
  
      private void handleShowNotification(int notificationId) {
          Application application = getApplication();
          NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
          Notification notification;
  
          // If the application is not the host - show error and return.
          if (!(application instanceof RhythmControl.Host)) {
              notification = makeCommonNotification(getString(R.string.arl_no_host_text))
                      .setColor(NOTIFICATION_ERROR_COLOR)
                      .setContentTitle(getString(R.string.arl_no_host_title))
                      .build();
              manager.notify(notificationId, notification);
              return;
          }
  
          RhythmControl control = ((RhythmControl.Host) application).getRhythmControl();
          RhythmGroup currentGroup = control.getCurrentNotificationGroup();
  
          // If there are no groups yet - show warning and return.
          if (currentGroup == null) {
              notification = makeCommonNotification(getString(R.string.arl_no_groups_text))
                      .setColor(NOTIFICATION_ERROR_COLOR)
                      .setContentTitle(getString(R.string.arl_no_groups_title))
                      .build();
              manager.notify(notificationId, notification);
              return;
          }
  
          // Now if everything is OK:
          Intent nextGroupAction = new Intent(this, RhythmNotificationService.class);
          nextGroupAction.setAction(ACTION_NEXT_GROUP);
          PendingIntent piNextGroupAction = PendingIntent.getService(this, 0, nextGroupAction, PendingIntent.FLAG_UPDATE_CURRENT);
  
          Intent nextOverlayAction = new Intent(this, RhythmNotificationService.class);
          nextOverlayAction.setAction(ACTION_NEXT_OVERLAY);
          PendingIntent piNextOverlayAction = PendingIntent.getService(this, 0, nextOverlayAction, PendingIntent.FLAG_UPDATE_CURRENT);
  
          Intent dismissAction = new Intent(this, RhythmNotificationService.class);
          dismissAction.setAction(ACTION_DISMISS_QUICK_CONTROL);
          PendingIntent piDismissAction = PendingIntent.getService(this, 0, dismissAction, PendingIntent.FLAG_UPDATE_CURRENT);
  
          // todo: another action when notification is clicked (control activity will be added in v1.0)
  
          // Determine what to write in notification
          RhythmOverlay currentOverlay = currentGroup.getCurrentOverlay();
          String groupText = getString(R.string.arl_group, currentGroup.toString());
          String overlayText = currentOverlay == null ?
                  getString(R.string.arl_no_overlay) : getString(R.string.arl_overlay, currentOverlay.toString());
  
          // Finally, build and display the notification
          notification = makeCommonNotification(overlayText)
                  .setColor(NOTIFICATION_ICON_COLOR)
                  .setContentTitle(groupText)
                  .setDeleteIntent(piDismissAction)
                  .addAction(new NotificationCompat.Action(R.drawable.arl_loop, getString(R.string.arl_next_group), piNextGroupAction))
                  .addAction(new NotificationCompat.Action(R.drawable.arl_loop, getString(R.string.arl_next_overlay), piNextOverlayAction))
                  .build();
          manager.notify(notificationId, notification);
      }
  
      private NotificationCompat.Builder makeCommonNotification(String text) {
          final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                  .setSmallIcon(R.drawable.arl_rhythm)
                  .setCategory(NotificationCompat.CATEGORY_SERVICE)
                  .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                  .setAutoCancel(false)
                  .setShowWhen(false)
                  .setContentText(text)
                  .setStyle(new NotificationCompat.BigTextStyle().bigText(text));
  
          // Old androids throw an exception when the notification doesn't have content intent
          if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
              Intent contentAction = new Intent(this, RhythmNotificationService.class);
              contentAction.setAction(ACTION_NEXT_OVERLAY);
              PendingIntent piContentAction = PendingIntent.getService(this, 0, contentAction, PendingIntent.FLAG_UPDATE_CURRENT);
              builder.setContentIntent(piContentAction);
          }
  
          return builder;
      }
  
      private void handleNextGroup() {
          Application application = getApplication();
          if (application instanceof RhythmControl.Host) {
              final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
              Handler handler = new Handler(Looper.getMainLooper());
              // Not using anonymous classes here to avoid leaking context
              handler.post(new NextGroupRunnable(rhythmControl));
          }
      }
  
      private void handleNextOverlay() {
          Application application = getApplication();
          if (application instanceof RhythmControl.Host) {
              final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
              Handler handler = new Handler(Looper.getMainLooper());
              handler.post(new NextOverlayRunnable(rhythmControl.getCurrentNotificationGroup()));
          }
      }
  
      private void handleDismissQuickConfig() {
          Application application = getApplication();
          if (application instanceof RhythmControl.Host) {
              final RhythmControl rhythmControl = ((RhythmControl.Host) application).getRhythmControl();
              Handler handler = new Handler(Looper.getMainLooper());
              handler.post(new HideAllOverlaysRunnable(rhythmControl));
          }
      }
  
      /**
       * Runnable to dispatch next group call to the UI thread
       */
      private static class NextGroupRunnable implements Runnable {
  
          private RhythmControl mControl;
  
          public NextGroupRunnable(RhythmControl control) {
              mControl = control;
          }
  
          @Override
          public void run() {
              mControl.selectNextNotificationGroup();
          }
      }
  
      /**
       * Runnable to dispatch next overlay call to the UI thread
       */
      private static class NextOverlayRunnable implements Runnable {
  
          private RhythmGroup mGroup;
  
          public NextOverlayRunnable(RhythmGroup group) {
              mGroup = group;
          }
  
          @Override
          public void run() {
              mGroup.selectNextOverlay();
          }
      }
  
      /**
       * Runnable to dispatch hide all overlays call to the UI thread
       */
      private static class HideAllOverlaysRunnable implements Runnable {
  
          private RhythmControl mControl;
  
          public HideAllOverlaysRunnable(RhythmControl control) {
              mControl = control;
          }
  
          @Override
          public void run() {
              mControl.onNotificationDismiss();
          }
      }
  }
  