package Ġcom . sk yy o . samples . features . input Valid ations . auto De bounce Ċ Ċ import Ġandroidx . lifecycle . Saved State Handle Ċ import Ġandroidx . lifecycle . ViewModel Ċ import Ġandroidx . lifecycle . viewModel Scope Ċ import Ġcom . sk yy o . samples . R Ċ import Ġcom . sk yy o . samples . extensions . getState Flow Ċ import Ġcom . sk yy o . samples . features . input Valid ations .* Ċ import Ġdagger . h ilt . android . lifecycle . H ilt ViewModel Ċ import Ġkotlinx . coroutines . Dispatch ers Ċ import Ġkotlinx . coroutines . Flow Preview Ċ import Ġkotlinx . coroutines . channels . Channel Ċ import Ġkotlinx . coroutines . delay Ċ import Ġkotlinx . coroutines . flow .* Ċ import Ġkotlinx . coroutines . launch Ċ import Ġjavax . inject . Inject ĊĊ Ċ @ Opt In ( Flow Preview :: class ) Ċ @ H ilt ViewModel Ċ class ĠInput Validation Auto De bounce ViewModel Ġ@ Inject Ġconstructor ( ĊĠĠĠ Ġprivate Ġval Ġhandle : ĠSaved State Handle Ċ ) Ġ: ĠViewModel () Ġ{ ĊĊĠĠĠ Ġval Ġname Ġ= Ġhandle . getState Flow ( viewModel Scope , Ġ" name ", ĠInput Wrapper ()) ĊĠĠĠ Ġval Ġcredit Card Number Ġ= Ġhandle . getState Flow ( viewModel Scope , Ġ" credit Card Number ", ĠInput Wrapper ()) ĊĠĠĠ Ġval Ġare Inputs Valid Ġ= Ġcombine ( name , Ġcredit Card Number ) Ġ{ Ġname , Ġcard Number Ġ-> ĊĠĠĠĠĠĠĠ Ġname . value . isNotEmpty () Ġ&& Ġname . error Id Ġ== Ġnull Ġ&& ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcard Number . value . isNotEmpty () Ġ&& Ġcard Number . error Id Ġ== Ġnull ĊĠĠĠ Ġ}. state In ( viewModel Scope , ĠSh aring Started . While Sub scribed ( 5 0 0 0 L ), Ġfalse ) ĊĠĠĠ Ġprivate Ġvar Ġfocused TextField Ġ= Ġhandle . get (" focused TextField ") Ġ?: ĠF ocused TextField Key . NAME ĊĠĠĠĠĠĠĠ Ġset ( value ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfield Ġ= Ġvalue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġhandle . set (" focused TextField ", Ġvalue ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġ_ events Ġ= ĠChannel < Screen Event >() ĊĠĠĠ Ġval Ġevents Ġ= Ġ_ events . receive As Flow () ĊĠĠĠ Ġprivate Ġval Ġinput Events Ġ= ĠChannel < User Input Event >( Channel . CON FL ATED ) ĊĊĠĠĠ Ġinit Ġ{ ĊĠĠĠĠĠĠĠ Ġobserve User Input Events () ĊĠĠĠĠĠĠĠ Ġif Ġ( focused TextField Ġ!= ĠF ocused TextField Key . NONE ) Ġfocus On Last Selected TextField () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġobserve User Input Events () Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinput Events . receive As Flow () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. on Each Ġ{ Ġevent Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwhen Ġ( event ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġis ĠUser Input Event . Name Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwhen Ġ( Input Validator . getName Error Id OrNull ( event . input )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġname . value Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġname . value . copy ( value Ġ= Ġevent . input , Ġerror Id Ġ= Ġnull ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġ-> Ġname . value Ġ= Ġname . value . copy ( value Ġ= Ġevent . input ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġis ĠUser Input Event . CreditCard Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwhen Ġ( Input Validator . get Card Number Error Id OrNull ( event . input )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcredit Card Number . value Ġ= Ġcredit Card Number . value . copy ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġvalue Ġ= Ġevent . input , ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġerror Id Ġ= Ġnull ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġ-> Ġcredit Card Number . value Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcredit Card Number . value . copy ( value Ġ= Ġevent . input ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. de bounce ( 3 5 0 ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. collect Ġ{ Ġevent Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwhen Ġ( event ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġis ĠUser Input Event . Name Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġerror Id Ġ= ĠInput Validator . getName Error Id OrNull ( event . input ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġname . value Ġ= Ġname . value . copy ( error Id Ġ= Ġerror Id ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġis ĠUser Input Event . CreditCard Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġerror Id Ġ= ĠInput Validator . get Card Number Error Id OrNull ( event . input ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcredit Card Number . value Ġ= Ġcredit Card Number . value . copy ( error Id Ġ= Ġerror Id ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġon Name Entered ( input : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinput Events . send ( User Input Event . Name ( input )) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġon Card Number Entered ( input : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinput Events . send ( User Input Event . CreditCard ( input )) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġon TextField Focus Changed ( key : ĠF ocused TextField Key , Ġis Focused : ĠBoolean ) Ġ{ ĊĠĠĠĠĠĠĠ Ġfocused TextField Ġ= Ġif Ġ( is Focused ) Ġkey Ġelse ĠF ocused TextField Key . NONE ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġon Name I me Action Click () Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Move Focus ()) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġon Continue Click () Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġwhen Ġ( val Ġinput Errors Ġ= Ġget Input Errors OrNull ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull Ġ-> Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġclear Focus And Hide Keyboard () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Show Toast ( R . string . success )) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġ-> Ġdisplay Input Errors ( input Errors ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget Input Errors OrNull (): ĠInput Errors ? Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġname Error Id Ġ= ĠInput Validator . getName Error Id OrNull ( name . value . value ) ĊĠĠĠĠĠĠĠ Ġval Ġcard Error Id Ġ= ĠInput Validator . get Card Number Error Id OrNull ( credit Card Number . value . value ) ĊĠĠĠĠĠĠĠ Ġreturn Ġif Ġ( name Error Id Ġ== Ġnull Ġ&& Ġcard Error Id Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠInput Errors ( name Error Id , Ġcard Error Id ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġdisplay Input Errors ( input Errors : ĠInput Errors ) Ġ{ ĊĠĠĠĠĠĠĠ Ġname . value Ġ= Ġname . value . copy ( error Id Ġ= Ġinput Errors . name Error Id ) ĊĠĠĠĠĠĠĠ Ġcredit Card Number . value Ġ= Ġcredit Card Number . value . copy ( error Id Ġ= Ġinput Errors . card Error Id ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġsuspend Ġfun Ġclear Focus And Hide Keyboard () Ġ{ ĊĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Clear Focus ) ĊĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Update Keyboard ( false )) ĊĠĠĠĠĠĠĠ Ġfocused TextField Ġ= ĠF ocused TextField Key . NONE ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġfocus On Last Selected TextField () Ġ{ ĊĠĠĠĠĠĠĠ ĠviewModel Scope . launch ( Dispatch ers . Default ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Request Focus ( focused TextField )) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelay ( 2 5 0 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ_ events . send ( Screen Event . Update Keyboard ( true )) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} Ċ } ĊĊ