Skip to content

Commit

Permalink
- CollectionView, MoreViewHeader: adopt UIView.withScreenshotProtection
Browse files Browse the repository at this point in the history
- ShareViewController: remove redundant screenshot protection code
- BrowserNavigationViewController: fully adopt UIView.withScreenshotProtection
- ConfidentialContentView, SecureTextField: fix SwiftLint warnings
  • Loading branch information
felix-schwarz committed Jan 20, 2025
1 parent 568c49c commit c4a4927
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ open class CollectionViewController: UIViewController, UICollectionViewDelegate,
if usesStackViewRoot, let stackView = stackView {
let safeAreaView = ThemeCSSView(frame: view.bounds)
safeAreaView.translatesAutoresizingMaskIntoConstraints = false
safeAreaView.embed(toFillWith: collectionView, enclosingAnchors: safeAreaView.safeAreaAnchorSet)
safeAreaView.embed(toFillWith: collectionView.withScreenshotProtection, enclosingAnchors: safeAreaView.safeAreaAnchorSet)

stackView.addArrangedSubview(safeAreaView)
} else {
view.embed(toFillWith: collectionView, enclosingAnchors: view.defaultAnchorSet)
view.embed(toFillWith: collectionView.withScreenshotProtection, enclosingAnchors: view.defaultAnchorSet)
}
}

Expand Down Expand Up @@ -167,14 +167,10 @@ open class CollectionViewController: UIViewController, UICollectionViewDelegate,
public override func loadView() {
super.loadView()

let secureView = SecureTextField().secureContainerView

view.embed(toFillWith: secureView, enclosingAnchors: compressForKeyboard ? view.safeAreaWithKeyboardAnchorSet : view.safeAreaAnchorSet)

if usesStackViewRoot {
createStackView()
if let stackView {
secureView.embed(toFillWith: stackView, enclosingAnchors: compressForKeyboard ? view.safeAreaWithKeyboardAnchorSet : view.safeAreaAnchorSet)
view.embed(toFillWith: stackView, enclosingAnchors: compressForKeyboard ? view.safeAreaWithKeyboardAnchorSet : view.safeAreaAnchorSet)
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions ownCloudAppShared/Client/Sharing/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,7 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe

open override func viewDidLoad() {
super.viewDidLoad()

let wrappedContentContainerView = collectionView.withScreenshotProtection
view.addSubview(wrappedContentContainerView)


// Disable dragging of items, so keyboard control does
// not include "Drag Item" in the accessibility actions
// invoked with Tab + Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ open class BrowserNavigationViewController: EmbeddingViewController, Themeable,
contentContainerView.cssSelector = .content
contentContainerView.translatesAutoresizingMaskIntoConstraints = false
contentContainerView.focusGroupIdentifier = "com.owncloud.content"

let wrappedContentContainerView = contentContainerView.withScreenshotProtection
view.addSubview(wrappedContentContainerView)

Expand All @@ -91,20 +91,19 @@ open class BrowserNavigationViewController: EmbeddingViewController, Themeable,
navigationBarTopConstraint = navigationBarTopConstraint(for: navigationBarHidden)

NSLayoutConstraint.activate([

contentContainerView.topAnchor.constraint(equalTo: view.topAnchor),
contentContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
contentContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).with(priority: .defaultHigh), // Allow for flexibility without having to remove this constraint. It will be overridden by constraints with higher priority (default is .required) when necessary
contentContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

contentContainerLidView.topAnchor.constraint(equalTo: contentContainerView.topAnchor),
contentContainerLidView.bottomAnchor.constraint(equalTo: contentContainerView.bottomAnchor),
contentContainerLidView.leadingAnchor.constraint(equalTo: contentContainerView.leadingAnchor),
contentContainerLidView.trailingAnchor.constraint(equalTo: contentContainerView.trailingAnchor),

sideBarSeperatorView.topAnchor.constraint(equalTo: contentContainerView.topAnchor),
sideBarSeperatorView.bottomAnchor.constraint(equalTo: contentContainerView.bottomAnchor),
sideBarSeperatorView.leadingAnchor.constraint(equalTo: contentContainerView.leadingAnchor, constant: -1),
wrappedContentContainerView.topAnchor.constraint(equalTo: view.topAnchor),
wrappedContentContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
wrappedContentContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).with(priority: .defaultHigh), // Allow for flexibility without having to remove this constraint. It will be overridden by constraints with higher priority (default is .required) when necessary
wrappedContentContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

contentContainerLidView.topAnchor.constraint(equalTo: wrappedContentContainerView.topAnchor),
contentContainerLidView.bottomAnchor.constraint(equalTo: wrappedContentContainerView.bottomAnchor),
contentContainerLidView.leadingAnchor.constraint(equalTo: wrappedContentContainerView.leadingAnchor),
contentContainerLidView.trailingAnchor.constraint(equalTo: wrappedContentContainerView.trailingAnchor),

sideBarSeperatorView.topAnchor.constraint(equalTo: wrappedContentContainerView.topAnchor),
sideBarSeperatorView.bottomAnchor.constraint(equalTo: wrappedContentContainerView.bottomAnchor),
sideBarSeperatorView.leadingAnchor.constraint(equalTo: wrappedContentContainerView.leadingAnchor, constant: -1),
sideBarSeperatorView.widthAnchor.constraint(equalToConstant: 1),

navigationBarTopConstraint!,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,25 @@ public class ConfidentialContentView: UIView, Themeable {
setNeedsDisplay()
}
}

private var drawn: Bool = false

override init(frame: CGRect) {
super.init(frame: frame)
setupOrientationObserver()
Theme.shared.register(client: self, applyImmediately: true)
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupOrientationObserver()
}

deinit {
Theme.shared.unregister(client: self)
NotificationCenter.default.removeObserver(self)
}

private func setupOrientationObserver() {
NotificationCenter.default.addObserver(
self,
Expand All @@ -98,12 +98,12 @@ public class ConfidentialContentView: UIView, Themeable {
object: nil
)
}

@objc private func handleOrientationChange() {
drawn = false
setNeedsDisplay()
}

public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in subviews.reversed() {
let subviewPoint = convert(point, to: subview)
Expand All @@ -113,17 +113,17 @@ public class ConfidentialContentView: UIView, Themeable {
}
return nil // Allow touches to pass through
}

public override func draw(_ rect: CGRect) {
guard ConfidentialManager.shared.markConfidentialViews, let context = UIGraphicsGetCurrentContext() else { return }

drawn = true

context.saveGState()

let radians = angle * .pi / 180
context.rotate(by: radians)

let textAttributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: textColor
Expand All @@ -132,23 +132,23 @@ public class ConfidentialContentView: UIView, Themeable {
.font: font,
.foregroundColor: textColor
]

let textSize = text.size(withAttributes: textAttributes)
let subtextSize = subtext.size(withAttributes: subtextAttributes)

let stepX = textSize.width + columnSpacing
let stepY = textSize.height + lineSpacing

let stepSubtextX = subtextSize.width + columnSpacing
let stepSubTextY = subtextSize.height + lineSpacing

let rotatedDiagonal = sqrt(rect.width * rect.width + rect.height * rect.height)

let startX = -rotatedDiagonal
let startY = -rotatedDiagonal + marginY
let endX = rotatedDiagonal
let endY = rotatedDiagonal - marginY

var y = startY
while y <= endY {
var x = startX
Expand All @@ -165,17 +165,17 @@ public class ConfidentialContentView: UIView, Themeable {
}
y += stepY
}

context.restoreGState()

if angle < 45.0 {
let combinedText = "\(subtext) - \(text)"
let combinedTextAttributes: [NSAttributedString.Key: Any] = [
.font: subtitleFont,
.foregroundColor: subtitleTextColor
]
let combinedTextSize = combinedText.size(withAttributes: combinedTextAttributes)

var x = CGFloat(0)
let subtextY = rect.height - combinedTextSize.height - 2
while x < rect.width {
Expand All @@ -184,40 +184,40 @@ public class ConfidentialContentView: UIView, Themeable {
}
}
}

public func applyThemeCollection(theme: Theme, collection: ThemeCollection, event: ThemeEvent) {
if let color = collection.css.getColor(.stroke, selectors: [.confidentialLabel], for: nil) {
textColor = color
}
if let color = collection.css.getColor(.stroke, selectors: [.confidentialSecondaryLabel], for: nil) {
subtitleTextColor = color
}

drawn = false
setNeedsDisplay()
}
}

public extension UIView {

func secureView(core: OCCore?) {
let overlayView = ConfidentialContentView()
overlayView.text = core?.bookmark.user?.emailAddress ?? "Confidential View"
overlayView.subtext = core?.bookmark.userName ?? "Confidential View"
overlayView.backgroundColor = .clear
overlayView.translatesAutoresizingMaskIntoConstraints = false
overlayView.angle = (self.frame.height <= 200) ? 10 : 45

self.addSubview(overlayView)

NSLayoutConstraint.activate([
overlayView.topAnchor.constraint(equalTo: self.topAnchor),
overlayView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
overlayView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
overlayView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
])
}

var withScreenshotProtection: UIView {
if ConfidentialManager.shared.allowScreenshots {
return self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,41 @@ import UIKit
import ownCloudApp

class SecureTextField : UITextField {

override init(frame: CGRect) {
super.init(frame: .zero)
self.isSecureTextEntry = true
self.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

var secureContainerView: UIView {
guard !ConfidentialManager.shared.allowScreenshots else {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false

return view
}

if let secureView = self.subviews.filter({ subview in
type(of: subview).description().contains("CanvasView")
}).first {
secureView.translatesAutoresizingMaskIntoConstraints = false
secureView.isUserInteractionEnabled = true
return secureView
}

// If screenshot protection was not possible, force close the application.
exit(0)

let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false

return view
}

override var canBecomeFirstResponder: Bool { false }
override func becomeFirstResponder() -> Bool { false }
}
31 changes: 13 additions & 18 deletions ownCloudAppShared/User Interface/More/MoreViewHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ open class MoreViewHeader: UIView {

private func render() {
cssSelectors = [.more, .header]

let secureView = SecureTextField().secureContainerView

let contentContainerView = UIView()
contentContainerView.translatesAutoresizingMaskIntoConstraints = false
secureView.addSubview(contentContainerView)
self.addSubview(secureView)

let wrappedContentContainerView = contentContainerView.withScreenshotProtection
self.addSubview(wrappedContentContainerView)

titleLabel.translatesAutoresizingMaskIntoConstraints = false
detailLabel.translatesAutoresizingMaskIntoConstraints = false
Expand All @@ -110,18 +110,13 @@ open class MoreViewHeader: UIView {
titleLabel.setContentCompressionResistancePriority(.required, for: .vertical)
detailLabel.setContentCompressionResistancePriority(.required, for: .vertical)
labelContainerView.setContentCompressionResistancePriority(.required, for: .vertical)



NSLayoutConstraint.activate([
secureView.topAnchor.constraint(equalTo: self.topAnchor),
secureView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
secureView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
secureView.trailingAnchor.constraint(equalTo: self.trailingAnchor),

contentContainerView.topAnchor.constraint(equalTo: self.topAnchor),
contentContainerView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
contentContainerView.leadingAnchor.constraint(equalTo: self.leadingAnchor),

wrappedContentContainerView.topAnchor.constraint(equalTo: self.topAnchor),
wrappedContentContainerView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
wrappedContentContainerView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
wrappedContentContainerView.trailingAnchor.constraint(equalTo: self.trailingAnchor),

titleLabel.leadingAnchor.constraint(equalTo: labelContainerView.leadingAnchor),
titleLabel.trailingAnchor.constraint(equalTo: labelContainerView.trailingAnchor),
titleLabel.topAnchor.constraint(equalTo: labelContainerView.topAnchor),
Expand Down Expand Up @@ -152,7 +147,7 @@ open class MoreViewHeader: UIView {
if showFavoriteButton {
updateFavoriteButtonImage()
favoriteButton.addTarget(self, action: #selector(toogleFavoriteState), for: UIControl.Event.touchUpInside)
self.addSubview(favoriteButton)
contentContainerView.addSubview(favoriteButton)
favoriteButton.isPointerInteractionEnabled = true

NSLayoutConstraint.activate([
Expand All @@ -163,7 +158,7 @@ open class MoreViewHeader: UIView {
favoriteButton.leadingAnchor.constraint(equalTo: labelContainerView.trailingAnchor, constant: 10)
])
} else if showActivityIndicator {
self.addSubview(activityIndicator)
contentContainerView.addSubview(activityIndicator)

NSLayoutConstraint.activate([
activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor),
Expand Down Expand Up @@ -227,7 +222,7 @@ open class MoreViewHeader: UIView {
core?.vault.resourceManager?.start(iconRequest)

titleLabel.numberOfLines = 0

self.secureView(core: core)
}

Expand Down

0 comments on commit c4a4927

Please sign in to comment.