-
Notifications
You must be signed in to change notification settings - Fork 0
feat: ignore masking #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9fecac4
de6b7f0
e176e66
d450316
da7f775
1d4d2f5
e8aeb8e
0bd899a
50d780c
10fadca
c6c149d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import UIKit | ||
|
|
||
| @objc(LDMasking) | ||
| public class ObjcLDMasking: NSObject { | ||
| // Use explicit selectors so we control the Obj-C names. | ||
| @objc(maskView:) | ||
| public static func mask(view: UIView) { | ||
| view.ldMask() | ||
| } | ||
|
|
||
| @objc(unmaskView:) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you are using @objcMembers annotation this is redundant, remove it please
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I use for naming as well |
||
| public static func unmask(view: UIView) { | ||
| view.ldUnmask() | ||
| } | ||
|
|
||
| @objc(ignoreView:) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you are using @objcMembers annotation this is redundant, remove it please |
||
| public static func ignore(view: UIView) { | ||
| view.ldIgnore() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,15 +13,20 @@ final class MaskCollector { | |
| enum Constants { | ||
| static let maskiOS26ViewTypes = Set(["CameraUI.ChromeSwiftUIView"]) | ||
| } | ||
|
|
||
| struct Settings { | ||
| var maskiOS26ViewTypes: Set<String> | ||
| var maskTextInputs: Bool | ||
| var maskWebViews: Bool | ||
| var maskImages: Bool | ||
| var minimumAlpha: CGFloat | ||
| var maskClasses: Set<ObjectIdentifier> | ||
|
|
||
| var maskUIViews: Set<ObjectIdentifier> | ||
| var unmaskUIViews: Set<ObjectIdentifier> | ||
| var ignoreUIViews: Set<ObjectIdentifier> | ||
|
|
||
| var maskAccessibilityIdentifiers: Set<String> | ||
| var unmaskAccessibilityIdentifiers: Set<String> | ||
| var ignoreAccessibilityIdentifiers: Set<String> | ||
|
|
||
| init(privacySettings: PrivacySettings) { | ||
|
|
@@ -30,23 +35,51 @@ final class MaskCollector { | |
| self.maskWebViews = privacySettings.maskWebViews | ||
| self.maskImages = privacySettings.maskImages | ||
| self.minimumAlpha = privacySettings.minimumAlpha | ||
| self.maskClasses = privacySettings.buildMaskClasses() | ||
|
|
||
| self.maskUIViews = Set(privacySettings.maskUIViews.map(ObjectIdentifier.init)) | ||
| self.unmaskUIViews = Set(privacySettings.unmaskUIViews.map(ObjectIdentifier.init)) | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.ignoreUIViews = Set(privacySettings.ignoreUIViews.map(ObjectIdentifier.init)) | ||
|
|
||
| self.maskAccessibilityIdentifiers = Set(privacySettings.maskAccessibilityIdentifiers) | ||
| self.unmaskAccessibilityIdentifiers = Set(privacySettings.unmaskAccessibilityIdentifiers) | ||
| self.ignoreAccessibilityIdentifiers = Set(privacySettings.ignoreAccessibilityIdentifiers) | ||
| } | ||
|
|
||
|
|
||
| func shouldIgnore(_ view: UIView) -> Bool { | ||
| let viewType = type(of: view) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a little bit expensive cpu-wise, just a notice, since you are now relying on ObjectIdentifier as the main approach to identify the views, it is not a good idea to change the approch this time.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I use ObjectIdentifier from the type not from the instance. That's why I need viewType
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand, just a small notice, not asking for a change, we are good |
||
| if SessionReplayAssociatedObjects.shouldIgnoreUIView(view) == true { | ||
| return true | ||
| } | ||
|
|
||
| if ignoreUIViews.contains(ObjectIdentifier(viewType)) { | ||
| return true | ||
| } | ||
|
|
||
| if let accessibilityIdentifier = view.accessibilityIdentifier, | ||
| ignoreAccessibilityIdentifiers.contains(accessibilityIdentifier) { | ||
| return true | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| func shouldMask(_ view: UIView) -> Bool { | ||
| if let shouldUnmask = SessionReplayAssociatedObjects.shouldMaskUIView(view), | ||
| !shouldUnmask { | ||
| !shouldUnmask { | ||
| return false | ||
| } | ||
|
|
||
| if let accessibilityIdentifier = view.accessibilityIdentifier, | ||
| ignoreAccessibilityIdentifiers.contains(accessibilityIdentifier) { | ||
| unmaskAccessibilityIdentifiers.contains(accessibilityIdentifier) { | ||
| return false | ||
| } | ||
|
|
||
| let viewType = type(of: view) | ||
| let viewIdentifier = ObjectIdentifier(viewType) | ||
| if unmaskUIViews.contains(viewIdentifier) { | ||
| return false | ||
| } | ||
|
|
||
| let stringViewType = String(describing: viewType) | ||
mario-launchdarkly marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if maskiOS26ViewTypes.contains(stringViewType) { | ||
|
|
@@ -83,23 +116,19 @@ final class MaskCollector { | |
| return true | ||
| } | ||
|
|
||
| if SessionReplayAssociatedObjects.shouldMaskSwiftUI(view) ?? false { | ||
| return true | ||
| } | ||
|
|
||
| if SessionReplayAssociatedObjects.shouldMaskUIView(view) ?? false { | ||
| if maskUIViews.contains(viewIdentifier) { | ||
| return true | ||
| } | ||
|
|
||
| if let accessibilityIdentifier = view.accessibilityIdentifier, | ||
| maskAccessibilityIdentifiers.contains(accessibilityIdentifier) { | ||
| return true | ||
| } | ||
| return false | ||
|
|
||
| return SessionReplayAssociatedObjects.shouldMaskUIView(view) == true | ||
| } | ||
| } | ||
|
|
||
| var settings: Settings | ||
|
|
||
| public init(privacySettings: PrivacySettings) { | ||
|
|
@@ -118,26 +147,26 @@ final class MaskCollector { | |
| view.alpha >= settings.minimumAlpha | ||
| else { return } | ||
|
|
||
| //let layer = currentView.layer.presentation() ?? currentView.layer | ||
| guard !settings.shouldIgnore(view) else { return } | ||
|
|
||
| let effectiveFrame = rPresenation.convert(layer.frame, from: layer.superlayer) | ||
|
|
||
| let shouldMask = settings.shouldMask(view) | ||
| if shouldMask, let mask = createMask(rPresenation, root: root, layer: layer, scale: scale) { | ||
| var operation = MaskOperation(mask: mask, kind: .fill, effectiveFrame: effectiveFrame) | ||
| #if DEBUG | ||
| #if DEBUG | ||
| operation.accessibilityIdentifier = view.accessibilityIdentifier | ||
| #endif | ||
| #endif | ||
abelonogov-ld marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| result.append(operation) | ||
| return | ||
| } | ||
|
|
||
| if !isSystem(view: view, pLayer: layer) && !isTransparent(view: view, pLayer: layer), result.isNotEmpty { | ||
| // if view.accessibilityIdentifier != nil { | ||
| result.removeAll { | ||
| effectiveFrame.contains($0.effectiveFrame) | ||
| } | ||
| // } | ||
| result.removeAll { | ||
| effectiveFrame.contains($0.effectiveFrame) | ||
| } | ||
| } | ||
|
|
||
| if let sublayers = layer.sublayers?.sorted(by: { $0.zPosition < $1.zPosition }) { | ||
| sublayers.forEach(visit) | ||
| } | ||
|
|
@@ -153,16 +182,14 @@ final class MaskCollector { | |
| } | ||
|
|
||
| func isSystem(view: UIView, pLayer: CALayer) -> Bool { | ||
| return false | ||
| return false | ||
| } | ||
|
|
||
| func createMask(_ rPresenation: CALayer, root: CALayer, layer: CALayer, scale: CGFloat) -> Mask? { | ||
| let scale = 1.0 // scale is already in layers | ||
| // let rBounds = rPresenation.bounds | ||
| let scale = 1.0 | ||
| let lBounds = layer.bounds | ||
| guard lBounds.width > 0, lBounds.height > 0 else { return nil } | ||
|
|
||
| //let lPresenation = layer.presentation() ?? layer | ||
|
|
||
| if CATransform3DIsAffine(layer.transform) { | ||
| let corner0 = layer.convert(CGPoint.zero, to: root) | ||
| let corner1 = layer.convert(CGPoint(x: lBounds.width, y: 0), to: root) | ||
|
|
@@ -176,10 +203,8 @@ final class MaskCollector { | |
| tx: tx, | ||
| ty: ty).scaledBy(x: scale, y: scale) | ||
| return Mask.affine(rect: lBounds, transform: affineTransform) | ||
| } else { // 3D animations | ||
| // let corner0 = CGPoint.zero | ||
| // let corner1 = CGPoint(x: lBounds.width, y: 0) | ||
|
|
||
| } else { | ||
| // TODO: finish 3D animations | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove else if you are not implementing/handling this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will |
||
| } | ||
|
|
||
| return nil | ||
|
|
@@ -193,21 +218,5 @@ final class MaskCollector { | |
| y: min(corner1.y, corner2.y), | ||
| width: abs(corner2.x - corner1.x), | ||
| height: abs(corner2.y - corner1.y)) | ||
|
|
||
| } | ||
| } | ||
|
|
||
| extension PrivacySettings { | ||
| func buildMaskClasses() -> Set<ObjectIdentifier> { | ||
| let ids = Set(maskUIViews.map(ObjectIdentifier.init)) | ||
|
|
||
|
|
||
| // if privacySettings.maskTextInputs { | ||
| // [UITextField.self, UITextView.self, UIWebView.self, UISearchTextField.self, | ||
| // SwiftUI.UITextView.self, SwiftUI.UITextView.self].forEach { | ||
| // ids.insert(ObjectIdentifier($0)) | ||
| // } | ||
| // } | ||
| return ids | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you are using @objcMembers annotation this is redundant, remove it please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already but it also not as it assigns the name exported method