chromium/chrome/services/mac_notifications/notification_category_manager.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_SERVICES_MAC_NOTIFICATIONS_NOTIFICATION_CATEGORY_MANAGER_H_
#define CHROME_SERVICES_MAC_NOTIFICATIONS_NOTIFICATION_CATEGORY_MANAGER_H_

#import <Foundation/Foundation.h>
#import <UserNotifications/UserNotifications.h>

#include <optional>
#include <string>
#include <tuple>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"

namespace mac_notifications {

// This class manages notification categories for a given NotificationCenter.
// Notification categories on macOS describe all action buttons that should be
// shown on a notification. Each NotificationCenter has a global set of
// categories that can be used on notifications shown with it. This class
// manages that set and returns category identifiers for a given set of action
// buttons.
class NotificationCategoryManager {
 public:
  using Button = std::pair</*title*/ std::u16string,
                           /*placeholder*/ std::optional<std::u16string>>;
  using Buttons = std::vector<Button>;

  explicit NotificationCategoryManager(
      UNUserNotificationCenter* notification_center);
  NotificationCategoryManager(const NotificationCategoryManager&) = delete;
  NotificationCategoryManager& operator=(const NotificationCategoryManager&) =
      delete;
  ~NotificationCategoryManager();

  // Initializes notification categories from displayed notifications.
  void InitializeExistingCategories(NSArray<UNNotification*>* notifications,
                                    NSSet<UNNotificationCategory*>* categories);

  // Gets an existing category identifier that matches the given action buttons
  // or creates a new one. The returned identifier will stay valid until all
  // notifications using that category have been closed.
  NSString* GetOrCreateCategory(const std::string& notification_id,
                                const Buttons& buttons,
                                bool settings_button);

  // Releases the category used for |notification_ids|. This needs to be called
  // once the notifications have been closed to clean up unused categories.
  void ReleaseCategories(const std::vector<std::string>& notification_ids);

  // Releases all categories managed for |notification_center_|.
  void ReleaseAllCategories();

 private:
  FRIEND_TEST_ALL_PREFIXES(MacNotificationServiceUNTest,
                           InitializeDeliveredNotifications);
  FRIEND_TEST_ALL_PREFIXES(NotificationCategoryManagerTest,
                           InitializeExistingCategories);

  // Synchronizes the set of currently used notification categories with the
  // system NotificationCenter.
  void UpdateNotificationCenterCategories();

  using CategoryKey = std::pair<Buttons, /*settings_button*/ bool>;
  using CategoryEntry = std::pair<UNNotificationCategory*,
                                  /*refcount*/ int>;

  // Creates a new category that shows the buttons given via |key|.
  static UNNotificationCategory* CreateCategory(const CategoryKey& key);

  // Gets the CategoryKey used to create |category|.
  static CategoryKey GetCategoryKey(UNNotificationCategory* category);

  // Maps the set of notification action buttons to a category.
  base::flat_map<CategoryKey, CategoryEntry> buttons_category_map_;

  // Maps a notification id to its set of notification action buttons.
  base::flat_map<std::string, CategoryKey> notification_id_buttons_map_;

  UNUserNotificationCenter* __strong notification_center_;
};

}  // namespace mac_notifications

#endif  // CHROME_SERVICES_MAC_NOTIFICATIONS_NOTIFICATION_CATEGORY_MANAGER_H_