chromium/ios/chrome/widget_kit_extension/lockscreen_launcher_widget.swift

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

import Foundation
import SwiftUI
import WidgetKit

#if IOS_ENABLE_LOCKSCREEN_WIDGET
  #if IOS_AVAILABLE_LOCKSCREEN_WIDGET

    enum LockscreenLauncherWidgetType {
      case search, incognito, voiceSearch, dinoGame

      struct Configuration {
        let displayName: LocalizedStringKey, description: LocalizedStringKey, imageName: String,
          accessibilityLabel: LocalizedStringKey,
          widgetURL: URL

        var supportedFamilies: [WidgetFamily] {
          if #available(iOS 16, *) {
            return [.accessoryCircular]
          }
          return []
        }
      }

      var configuration: Configuration {
        switch self {
        case .search:
          return Configuration(
            displayName: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_SEARCH_DISPLAY_NAME",
            description: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_SEARCH_DESCRIPTION",
            imageName: "lockscreen_chrome_logo",
            accessibilityLabel:
              "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_SEARCH_A11Y_LABEL",
            widgetURL: WidgetConstants.LockscreenLauncherWidget.searchUrl)
        case .incognito:
          return Configuration(
            displayName: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_INCOGNITO_DISPLAY_NAME",
            description: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_INCOGNITO_DESCRIPTION",
            imageName: "lockscreen_incognito_logo",
            accessibilityLabel:
              "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_INCOGNITO_A11Y_LABEL",
            widgetURL: WidgetConstants.LockscreenLauncherWidget.incognitoUrl)
        case .voiceSearch:
          return Configuration(
            displayName:
              "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_VOICESEARCH_DISPLAY_NAME",
            description: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_VOICESEARCH_DESCRIPTION",
            imageName: "lockscreen_voicesearch_logo",
            accessibilityLabel:
              "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_VOICESEARCH_A11Y_LABEL",
            widgetURL: WidgetConstants.LockscreenLauncherWidget.voiceSearchUrl)
        case .dinoGame:
          return Configuration(
            displayName: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_GAME_DISPLAY_NAME",
            description: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_GAME_DESCRIPTION",
            imageName: "lockscreen_game_logo",
            accessibilityLabel: "IDS_IOS_WIDGET_KIT_EXTENSION_LOCKSCREEN_LAUNCHER_GAME_A11Y_LABEL",
            widgetURL: WidgetConstants.LockscreenLauncherWidget.gameUrl)
        }
      }
    }

    func lockScreenWidgetBackground() -> some View {
      if #available(iOS 16.0, *) {
        return AccessoryWidgetBackground()
      } else {
        // Widget only supports iOS16+
        return EmptyView()
      }
    }

    struct LockscreenLauncherWidgetEntryView: View {
      let entry: Provider.Entry
      let configuration: LockscreenLauncherWidgetType.Configuration

      var body: some View {
        let configuration = self.configuration
        ZStack {
          Image(configuration.imageName)
            .renderingMode(.template)
            .foregroundColor(.white)
        }
        .widgetURL(configuration.widgetURL)
        .accessibilityElement()
        .accessibilityLabel(configuration.accessibilityLabel)
        .crContainerBackground(lockScreenWidgetBackground())
      }
    }

    func lockscreenLauncherWidgetConfiguration(
      ofKind kind: String, forType type: LockscreenLauncherWidgetType
    ) -> some WidgetConfiguration {
      let configuration = type.configuration
      return StaticConfiguration(kind: kind, provider: Provider()) { entry in
        LockscreenLauncherWidgetEntryView(entry: entry, configuration: configuration)
      }
      .configurationDisplayName(
        Text(configuration.displayName)
      )
      .description(Text(configuration.description))
      .supportedFamilies(configuration.supportedFamilies)
      .crDisfavoredLocations()
      .crContainerBackgroundRemovable(false)
    }

    struct LockscreenLauncherSearchWidget: Widget {
      // Changing |kind| or deleting this widget will cause all installed instances of this widget
      // to stop updating and show the placeholder state.
      let kind: String = "LockscreenLauncherSearchWidget"

      var body: some WidgetConfiguration {
        lockscreenLauncherWidgetConfiguration(ofKind: kind, forType: .search)
      }
    }

    struct LockscreenLauncherIncognitoWidget: Widget {
      // Changing |kind| or deleting this widget will cause all installed instances of this widget
      // to stop updating and show the placeholder state.
      let kind: String = "LockscreenLauncherIncognitoWidget"

      var body: some WidgetConfiguration {
        lockscreenLauncherWidgetConfiguration(ofKind: kind, forType: .incognito)
      }
    }

    struct LockscreenLauncherVoiceSearchWidget: Widget {
      // Changing |kind| or deleting this widget will cause all installed instances of this widget
      // to stop updating and show the placeholder state.
      let kind: String = "LockscreenLauncherVoiceSearchWidget"

      var body: some WidgetConfiguration {
        lockscreenLauncherWidgetConfiguration(ofKind: kind, forType: .voiceSearch)
      }
    }

    struct LockscreenLauncherGameWidget: Widget {
      // Changing |kind| or deleting this widget will cause all installed instances of this widget
      // to stop updating and show the placeholder state.
      let kind: String = "LockscreenLauncherGameWidget"

      var body: some WidgetConfiguration {
        lockscreenLauncherWidgetConfiguration(ofKind: kind, forType: .dinoGame)
      }
    }

  #endif  // IOS_AVAILABLE_LOCKSCREEN_WIDGET
#endif  // IOS_ENABLE_LOCKSCREEN_WIDGET