Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Enhancing Applications with Accessibility API
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Kishikawa Katsumi
March 20, 2024
Programming
6.4k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Enhancing Applications with Accessibility API
Kishikawa Katsumi
March 20, 2024
More Decks by Kishikawa Katsumi
See All by Kishikawa Katsumi
Accessibility APIでテキストの内容、位置、サイズを取得する実装パターン
kishikawakatsumi
0
89
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
OCRを使ってゲームのアイテムをデータ化する
kishikawakatsumi
0
200
Running Swift without an OS
kishikawakatsumi
0
1.2k
浮動小数の比較について
kishikawakatsumi
0
640
Automatic Grammar Agreementと Markdown Extended Attributes について
kishikawakatsumi
0
280
愛される翻訳の秘訣
kishikawakatsumi
3
500
Private APIの呼び出し方
kishikawakatsumi
3
1.1k
iOSでSVG画像を扱う
kishikawakatsumi
0
270
Other Decks in Programming
See All in Programming
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
140
Heart of Swift Concurrency
koher
0
1.2k
Verilogで学ぶCPU自作入門.pdf
uyuki234
8
4k
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
33
15k
iOSDCのペンライトを自動制御したい!
akkeylab
0
360
Jetpack Compose Mechanisms
skydoves
2
260
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
440
mrbgem 三角測量 開発
ogom
0
190
Embedded Swiftで作る自作USBデバイス によるiOSデバイスの自動テスト / iOSDC Japan 2026 glassfiber
glassfiber
0
290
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
170
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
150
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
480
Featured
See All Featured
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
420
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
KATA
mclloyd
PRO
35
16k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
The Curious Case for Waylosing
cassininazir
1
550
Speed Design
sergeychernyshev
33
2.1k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
860
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
450
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
700
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Transcript
&OIBODJOH"QQMJDBUJPOTXJUI "DDFTTJCJMJUZ"1* LJTIJLBXBLBUTVNJ !LJTIJLBXBLBUTVNJ!IBDIZEFSNJP LJTIJLBXBLBUTVNJ
IUUQTDPEFWJTVBMTUVEJPDPNVQEBUFTW@@TUSFBNJOHJOMJOFDIBU
IUUQTDPEFWJTVBMTUVEJPDPNVQEBUFTW@@IPMEUPTQFBLNPEF
*OUFHSBUF"*"TTJTUBOUT*OUP"OZ"QQT r *OZPVSGBWPSJUFUFYUFEJUPS r *OZPVSXFCCSPXTFS r 0SDPNJDSFBEFST
4BNQMF$PEF ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU
None
$PSF5FDIOPMPHJFT r 3FUSJFWFTFMFDUFEUFYUGPSBOZBQQMJDBUJPO r .POJUPSHMPCBMLFZNPVTFFWFOUT
"DDFTTJCJMJUZ"1* r "MMPXTBDDFTTUP6*FMFNFOUTPGUIFTZTUFNPSPUIFSBQQMJDBUJPOT r &OBCMFTNBOJQVMBUJPOPG6*FMFNFOUTPGPUIFSBQQMJDBUJPOT
&OBCMJOH"DDFTTJCJMJUZ"1* 4BOECPYBOE1SJWBDZ4FUUJOHT r %JTBCMF4BOECPY r (SBOU"DDFTTJCJMJUZ"1*1FSNJTTJPOT
%JTBCMF4BOECPY
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO 1SJWBDZ4FUUJOHT
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @ [kAXTrustedCheckOptionPrompt: true]
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
(SBOU"DDFTTJCJMJUZ1FSNJTTJPO "9*41SPDFTT5SVTUFE8JUI0QUJPOT @
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ 5IF6*FMFNFOUDPOUBJOJOHUIFWBMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ ,FZPGUIFWBMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ 7BMVFZPVXBOUUPSFUSJFWF
#BTJD6TBHF "96*&MFNFOU$PQZ"UUSJCVUF7BMVF @@@ &SSPSDPEF
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
4ZTUFN8JEF"96*&MFNFOU "96*&MFNFOU$SFBUF4ZTUFN8JEF let systemWideElement: AXUIElement = AXUIElementCreateSystemWide()
4ZTUFN8JEF"96*&MFNFOU r "DDFTTTZTUFNXJEFLFZCPBSEGPDVT NPVTFDVSTPSQPTJUJPO BOEPUIFS JOGPSNBUJPO r "DDFTTTZTUFNMFWFM6*FMFNFOUTUIBUEPOPUCFMPOHUPBTQFDJ fi D
BQQMJDBUJPO FH NFOVCBS OPUJ fi DBUJPODFOUFS r 3FUSJFWFJOGPSNBUJPOBCPVUUIFDPOUFYUPGUIFDVSSFOUVTFSJOUFSBDUJPO TVDIBTUIFBDUJWFXJOEPXPSUIF6*FMFNFOUUIBUDVSSFOUMZIBTGPDVT
3FUSJFWF'PDVTFE6*&MFNFOU L"9'PDVTFE6*&MFNFOU"UUSJCVUF let systemWideElement = AXUIElementCreateSystemWide() var focusedElement: AnyObject? let
focusedElementError = AXUIElementCopyAttributeValue( systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement )
3FUSJFWF4FMFDUFE5FYU L"94FMFDUFE5FYU"UUSJCVUF var selectedTextValue: AnyObject? let selectedTextValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedTextValue ) guard let selectedTextValue, selectedTextValueError == .success else { return }
None
.POJUPS(MPCBM,FZ&WFOU /4&WFOUBEE(MPCBM.POJUPS'PS&WFOUT NBUDIJOHIBOEMFS open class NSEvent : NSObject, NSCopying, NSCoding
{ open class func addGlobalMonitorForEvents( matching mask: NSEvent.EventTypeMask, handler block: @escaping (NSEvent) -> Void ) -> Any? }
.POJUPS(MPCBM,FZ&WFOU NSEvent.addGlobalMonitorForEvents( matching: [.keyDown] ) { (event) in switch event.type
{ case .keyDown: guard event.modifierFlags.contains([.command, .control]) && event.keyCode == kVK_ANSI_A else { return } ... default: break } }
.POJUPS(MPCBM,FZ&WFOU NSEvent.addGlobalMonitorForEvents( matching: [.keyDown] ) { (event) in switch event.type
{ case .keyDown: guard event.modifierFlags.contains([.command, .control]) && event.keyCode == kVK_ANSI_A else { return } ... default: break } }
.POJUPS(MPCBM,FZ&WFOU $BSCPO"1* *OTUBMM&WFOU)BOEMFS 3FHJTUFS&WFOU)PU,FZ ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r *OTUBMM&WFOU)BOEMFS @@@@@@
r 3FHJTUFS&WFOU)PU,FZ @@@@@@ r $BSCPO"1* r /PBDDFTTJCJMJUZQFSNJTTJPOSFRVJSFE
.POJUPS(MPCBM,FZ&WFOU &WFOU5BQ ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU let mask: CGEventMask = (1 << CGEventType.keyDown.rawValue)
let tap = CGEvent.tapCreate( tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: mask, callback: { (proxy, type, event, refcon) in ... }, userInfo: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) ) let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0) CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) CGEvent.tapEnable(tap: tap!, enable: true)
.POJUPS(MPCBM,FZ&WFOU ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r $BOTUPQFWFOUQSPQBHBUJPO r $BONPEJGZFWFOU &WFOU5BQ
None
3FUSJFWF4FMFDUFE5FYU 'SPN8FC7JFX var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
3FUSJFWF4FMFDUFE5FYU 'SPN8FC7JFX var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement
as! AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
None
"96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as!
AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as! AXUIElement,
"AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ... "96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF
var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as! AXUIElement,
"AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ... "96*&MFNFOU$PQZ1BSBNFUFSJ[FE"UUSJCVUF7BMVF
)PXUP,OPX6OEPDVNFOUFE,FZT var selectedTextMarkerRangeValue: AnyObject? let selectedTextMarkerRangeValueError = AXUIElementCopyAttributeValue( focusedElement as!
AXUIElement, "AXSelectedTextMarkerRange" as CFString, &selectedTextMarkerRangeValue ) ... var stringForTextMarkerRangeValue: AnyObject? let stringForTextMarkerRangeValueError = AXUIElementCopyParameterizedAttributeValue( focusedElement as! AXUIElement, "AXStringForTextMarkerRange" as CFString, selectedTextMarkerRangeValue, &stringForTextMarkerRangeValue ) ...
)PXUP,OPX6OEPDVNFOUFE,FZT "96*&MFNFOU$PQZ"UUSJCVUF/BNFT @@
None
None
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes ) let app = NSRunningApplication .runningApplications(withBundleIdentifier: "com.apple.TextEdit") .first
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes )
/PUJGJDBUJPOT var observer: AXObserver? let observerCreateError = AXObserverCreate( app.processIdentifier, {
(observer, element, notification, userData) in ... }, &observer ) AXObserverAddNotification( observer, appElement, kAXSelectedTextChangedNotification as CFString, nil ) CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), .commonModes ) kAXSelectedTextChangedNotification kAXValueChangedNotification ...
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
.PEJGZ5FYU var isAttributeSettableValue = DarwinBoolean(false) let isAttributeSettableValueError = AXUIElementIsAttributeSettable( element,
kAXValueAttribute as CFString, &isAttributeSettableValue ) guard isAttributeSettableValueError == .success else { return } if isAttributeSettableValue.boolValue { AXUIElementSetAttributeValue( element, kAXValueAttribute as CFString, "\(precedingTextValue)\n\(codeBlock)" as CFString ) return }
None
None
4$4DSFFOTIPU.BOBHFSDBQUVSF*NBHF func captureScreen(rect: CGRect) async throws -> CGImage? {
let content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) guard let mainDisplayID = NSScreen.main?.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID, let display = content.displays.first(where: { $0.displayID == mainDisplayID }) else { return nil } let streamConfig = SCStreamConfiguration() streamConfig.captureResolution = .automatic streamConfig.sourceRect = rect streamConfig.preservesAspectRatio = true streamConfig.scalesToFit = true streamConfig.capturesAudio = false streamConfig.excludesCurrentProcessAudio = true let filter = SCContentFilter(display: display, excludingWindows: []) let capturedImage = try await SCScreenshotManager.captureImage( contentFilter: filter, configuration: streamConfig ) return capturedImage }
3FUSJFWFBO*NBHF $(8JOEPX-JTU$SFBUF*NBHF @@@@ func captureScreen(rect: CGRect) -> CGImage? { let
screenShot = CGWindowListCreateImage( rect, [.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID, [.boundsIgnoreFraming, .bestResolution] ) return screenShot }
(SBOU4DSFFO3FDPSEJOH1FSNJTTJPO 1SJWBDZ4FUUJOHT
(SBOU4DSFFO3FDPSEJOH1FSNJTTJPO $(1SF fl JHIU4DSFFO$BQUVSF"DDFTT r $(1SF fl JHIU4DSFFO$BQUVSF"DDFTT r
*OEJDBUFTXIFUIFSTDSFFOSFDPSEJOHJTBMMPXFE r $(3FRVFTU4DSFFO$BQUVSF"DDFTT r 1SFTFOUUIFTZTUFNQSPNQU
&YUSBDUUIF5FYUGSPNBO*NBHF ϒϥοΫδϟοΫʹΑΖ͘͠ ࠤ౻लๆ
7JTJPO,JU ϒϥοΫδϟοΫʹΑΖ͘͠ ࠤ౻लๆ
&YUSBDUUIF5FYUGSPNBO*NBHF *NBHF"OBMZ[FS let analyzer = ImageAnalyzer() let analysis = try
await analyzer.analyze( image, orientation: .up, configuration: configuration ) let transcript = analysis.transcript
&YUSBDUUIF5FYUGSPNBO*NBHF *NBHF"OBMZ[FS let analyzer = ImageAnalyzer() let analysis = try
await analyzer.analyze( image, orientation: .up, configuration: configuration ) let transcript = analysis.transcript
None
8SBQ6Q r 8JOEPX$IBU"TTJTUBOU r #BTJD6TBHFPG"DDFTTJCJMJUZ"1* r *OMJOF$IBU"TTJTUBOU r /PUJ fi
DBUJPOT r .PEJGZ$POUFOU r $PNJD5SBOTMBUPS r 4DSFFO$BQUVSF"1*BOE0$3 r "96*&MFNFOU*OTQFDUPS r *OTQFDU"UUSJCVUF/BNFPG"96*&MFNFOU
4BNQMF$PEF ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU
3FGFSFODFT r 4BNQMF$PEF IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJUSZTXJGU r "DDFTTJOHUFYUWBMVFGSPNBOZ4ZTUFNXJEF"QQMJDBUJPOWJB"DDFTTJCJMJUZ "1* IUUQTNBDEFWFMPQFSTXPSEQSFTTDPNBDDFTTJOHUFYUWBMVFGSPNBOZTZTUFNXJEFBQQMJDBUJPOWJB BDDFTTJCJMJUZBQJ r
)PXUPHFUTFMFDUFEUFYUBOEJUTDPPSEJOBUFTGSPNBOZTZTUFNXJEFBQQMJDBUJPOVTJOH "DDFTTJCJMJUZ "1* IUUQTNBDEFWFMPQFSTXPSEQSFTTDPNIPXUPHFUTFMFDUFEUFYUBOEJUTDPPSEJOBUFTGSPNBOZTZTUFNXJEF BQQMJDBUJPOVTJOHBDDFTTJCJMJUZBQJ r 8IZBSFOUUIFNPTUVTFGVM.BDBQQTPOUIF"QQ4UPSF IUUQTBMJOQBOBJUJVDPNCMPHBQQTPVUTJEFBQQTUPSF r "MMBCPVUNBD04FWFOUPCTFSWBUJPO IUUQTEPDTHPPHMFDPNQSFTFOUBUJPOEO&BJ16EVIWKLTS%735D+B&6-C488IU7E()'@946IUNMQSFTFOU