blob: f97f786adbce2b98c6b0c065e5faf0898c8f5b27 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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
struct QuickActionsWidget: 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 = "QuickActionsWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
QuickActionsWidgetEntryView(entry: entry)
}
.configurationDisplayName(
Text("IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_DISPLAY_NAME")
)
.description(Text("IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_DESCRIPTION"))
.supportedFamilies([.systemMedium])
}
}
struct QuickActionsWidgetEntryView: View {
var entry: Provider.Entry
@Environment(\.redactionReasons) var redactionReasons
private let searchAreaHeight: CGFloat = 92
private let separatorHeight: CGFloat = 32
private let incognitoA11yLabel: LocalizedStringKey =
"IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_INCOGNITO_A11Y_LABEL"
private let voiceSearchA11yLabel: LocalizedStringKey =
"IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_VOICE_SEARCH_A11Y_LABEL"
private let qrA11yLabel: LocalizedStringKey =
"IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_QR_SCAN_A11Y_LABEL"
var body: some View {
VStack(spacing: 0) {
ZStack {
Color("widget_background_color")
.unredacted()
VStack {
Spacer()
Link(destination: WidgetConstants.QuickActionsWidget.searchUrl) {
ZStack {
RoundedRectangle(cornerRadius: 26)
.frame(height: 52)
.foregroundColor(Color("widget_search_bar_color"))
HStack(spacing: 12) {
Image("widget_chrome_logo")
.clipShape(Circle())
// Without .clipShape(Circle()), in the redacted/placeholder
// state the Widget shows an rectangle placeholder instead of
// a circular one.
.padding(.leading, 8)
.unredacted()
Text("IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_TITLE")
.font(.subheadline)
.foregroundColor(Color("widget_text_color"))
Spacer()
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding([.leading, .trailing], 11)
}
.accessibility(
label:
Text(
"IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_SEARCH_A11Y_LABEL"
)
)
Spacer()
}
.frame(height: searchAreaHeight)
}
ZStack {
Rectangle()
.foregroundColor(Color("widget_actions_row_background_color"))
.frame(minWidth: 0, maxWidth: .infinity)
HStack {
// Show interactive buttons if the widget is fully loaded, and show
// the custom placeholder otherwise.
if redactionReasons.isEmpty {
Link(destination: WidgetConstants.QuickActionsWidget.incognitoUrl) {
Image("widget_incognito_icon")
.frame(minWidth: 0, maxWidth: .infinity)
}
.accessibility(label: Text(incognitoA11yLabel))
Separator(height: separatorHeight)
Link(
destination: WidgetConstants.QuickActionsWidget.voiceSearchUrl
) {
Image("widget_voice_search_icon")
.frame(minWidth: 0, maxWidth: .infinity)
}
.accessibility(label: Text(voiceSearchA11yLabel))
Separator(height: separatorHeight)
Link(destination: WidgetConstants.QuickActionsWidget.qrCodeUrl) {
Image("widget_qr_icon")
.frame(minWidth: 0, maxWidth: .infinity)
}
.accessibility(label: Text(qrA11yLabel))
} else {
ButtonPlaceholder()
Separator(height: separatorHeight)
ButtonPlaceholder()
Separator(height: separatorHeight)
ButtonPlaceholder()
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding([.leading, .trailing], 11)
}
}
}
}
struct Separator: View {
let height: CGFloat
var body: some View {
RoundedRectangle(cornerRadius: 1)
.foregroundColor(Color("widget_separator_color"))
.frame(width: 2, height: height)
}
}
struct ButtonPlaceholder: View {
private let widthOfPlaceholder: CGFloat = 28
var body: some View {
Group {
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: widthOfPlaceholder, height: widthOfPlaceholder)
.foregroundColor(Color("widget_text_color"))
.opacity(0.3)
}.frame(minWidth: 0, maxWidth: .infinity)
}
}