DebugUIMessages.m 246 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860
  1. //
  2. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
  3. //
  4. #import "DebugUIMessages.h"
  5. #import "DebugContactsUtils.h"
  6. #import "DebugUIContacts.h"
  7. #import "DebugUIMessagesAction.h"
  8. #import "DebugUIMessagesAssetLoader.h"
  9. #import "OWSTableViewController.h"
  10. #import "Signal-Swift.h"
  11. #import <SignalCoreKit/NSDate+OWS.h>
  12. #import <SignalCoreKit/Randomness.h>
  13. #import <SignalMessaging/Environment.h>
  14. #import <SignalServiceKit/MIMETypeUtil.h>
  15. #import <SignalServiceKit/OWSBatchMessageProcessor.h>
  16. #import <SignalServiceKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
  17. #import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
  18. #import <SignalServiceKit/OWSMessageUtils.h>
  19. #import <SignalServiceKit/OWSPrimaryStorage+SessionStore.h>
  20. #import <SignalServiceKit/OWSSyncGroupsRequestMessage.h>
  21. #import <SignalServiceKit/OWSVerificationStateChangeMessage.h>
  22. #import <SignalServiceKit/TSIncomingMessage.h>
  23. #import <SignalServiceKit/TSInvalidIdentityKeyReceivingErrorMessage.h>
  24. #import <SignalServiceKit/TSOutgoingMessage.h>
  25. #import <SignalServiceKit/TSThread.h>
  26. NS_ASSUME_NONNULL_BEGIN
  27. @interface TSIncomingMessage (DebugUI)
  28. @property (nonatomic, getter=wasRead) BOOL read;
  29. @end
  30. #pragma mark -
  31. @interface TSOutgoingMessage (PostDatingDebug)
  32. - (void)setReceivedAtTimestamp:(uint64_t)value;
  33. @end
  34. #pragma mark -
  35. @implementation DebugUIMessages
  36. #pragma mark - Factory Methods
  37. - (NSString *)name
  38. {
  39. return @"Messages";
  40. }
  41. #ifdef DEBUG
  42. - (NSArray<OWSTableItem *> *)itemsForActions:(NSArray<DebugUIMessagesAction *> *)actions
  43. {
  44. NSMutableArray<OWSTableItem *> *items = [NSMutableArray new];
  45. for (DebugUIMessagesAction *action in actions) {
  46. [items addObject:[OWSTableItem itemWithTitle:action.label
  47. actionBlock:^{
  48. // For "all in group" actions, do each subaction in the group
  49. // exactly once, in a predictable order.
  50. if ([action isKindOfClass:[DebugUIMessagesGroupAction class]]) {
  51. DebugUIMessagesGroupAction *groupAction
  52. = (DebugUIMessagesGroupAction *)action;
  53. if (groupAction.subactionMode == SubactionMode_Ordered) {
  54. [action prepareAndPerformNTimes:groupAction.subactions.count];
  55. return;
  56. }
  57. }
  58. [DebugUIMessages performActionNTimes:action];
  59. }]];
  60. }
  61. return items;
  62. }
  63. #endif
  64. - (nullable OWSTableSection *)sectionForThread:(nullable TSThread *)thread
  65. {
  66. OWSAssertDebug(thread);
  67. NSMutableArray<OWSTableItem *> *items = [NSMutableArray new];
  68. #ifdef DEBUG
  69. [items addObject:[OWSTableItem itemWithTitle:@"Delete all messages in thread"
  70. actionBlock:^{
  71. [DebugUIMessages deleteAllMessagesInThread:thread];
  72. }]];
  73. [items addObject:[OWSTableItem itemWithTitle:@"👷 Thrash insert/deletes"
  74. actionBlock:^{
  75. [DebugUIMessages thrashInsertAndDeleteForThread:(TSThread *)thread
  76. counter:300];
  77. }]];
  78. [items addObjectsFromArray:[self itemsForActions:@[
  79. [DebugUIMessages fakeAllContactShareAction:thread],
  80. [DebugUIMessages sendMessageVariationsAction:thread],
  81. // Send Media
  82. [DebugUIMessages sendAllMediaAction:thread],
  83. [DebugUIMessages sendRandomMediaAction:thread],
  84. // Fake Media
  85. [DebugUIMessages fakeAllMediaAction:thread],
  86. [DebugUIMessages fakeRandomMediaAction:thread],
  87. // Fake Text
  88. [DebugUIMessages fakeAllTextAction:thread],
  89. [DebugUIMessages fakeRandomTextAction:thread],
  90. // Sequences
  91. [DebugUIMessages allFakeSequencesAction:thread],
  92. // Quoted Replies
  93. [DebugUIMessages allQuotedReplyAction:thread],
  94. // Exemplary
  95. [DebugUIMessages allFakeAction:thread],
  96. [DebugUIMessages allFakeBackDatedAction:thread],
  97. ]]];
  98. [items addObjectsFromArray:@[
  99. #pragma mark - Actions
  100. [OWSTableItem itemWithTitle:@"Send N text messages (1/sec.)"
  101. actionBlock:^{
  102. [DebugUIMessages sendNTextMessagesInThread:thread];
  103. }],
  104. [OWSTableItem itemWithTitle:@"Send Media Gallery"
  105. actionBlock:^{
  106. [DebugUIMessages sendMediaAlbumInThread:thread];
  107. }],
  108. [OWSTableItem itemWithTitle:@"Send Exemplary Media Galleries"
  109. actionBlock:^{
  110. [DebugUIMessages sendExemplaryMediaGalleriesInThread:thread];
  111. }],
  112. [OWSTableItem itemWithTitle:@"Select Fake"
  113. actionBlock:^{
  114. [DebugUIMessages selectFakeAction:thread];
  115. }],
  116. [OWSTableItem itemWithTitle:@"Select Send Media"
  117. actionBlock:^{
  118. [DebugUIMessages selectSendMediaAction:thread];
  119. }],
  120. [OWSTableItem itemWithTitle:@"Send All Contact Shares"
  121. actionBlock:^{
  122. [DebugUIMessages sendAllContacts:thread];
  123. }],
  124. [OWSTableItem itemWithTitle:@"Select Quoted Reply"
  125. actionBlock:^{
  126. [DebugUIMessages selectQuotedReplyAction:thread];
  127. }],
  128. [OWSTableItem itemWithTitle:@"Select Back-Dated"
  129. actionBlock:^{
  130. [DebugUIMessages selectBackDatedAction:thread];
  131. }],
  132. #pragma mark - Misc.
  133. [OWSTableItem itemWithTitle:@"Perform 100 random actions"
  134. actionBlock:^{
  135. [DebugUIMessages performRandomActions:100 thread:thread];
  136. }],
  137. [OWSTableItem itemWithTitle:@"Perform 1,000 random actions"
  138. actionBlock:^{
  139. [DebugUIMessages performRandomActions:1000 thread:thread];
  140. }],
  141. [OWSTableItem itemWithTitle:@"Create 10 fake messages"
  142. actionBlock:^{
  143. [DebugUIMessages sendFakeMessages:10 thread:thread];
  144. }],
  145. [OWSTableItem itemWithTitle:@"Create 1 fake thread with 1 message"
  146. actionBlock:^{
  147. [DebugUIMessages createFakeThreads:1 withFakeMessages:1];
  148. }],
  149. [OWSTableItem itemWithTitle:@"Create 100 fake threads with 10 messages"
  150. actionBlock:^{
  151. [DebugUIMessages createFakeThreads:100 withFakeMessages:10];
  152. }],
  153. [OWSTableItem itemWithTitle:@"Create 10 fake threads with 100 messages"
  154. actionBlock:^{
  155. [DebugUIMessages createFakeThreads:10 withFakeMessages:100];
  156. }],
  157. [OWSTableItem itemWithTitle:@"Create 10 fake threads with 10 messages"
  158. actionBlock:^{
  159. [DebugUIMessages createFakeThreads:10 withFakeMessages:10];
  160. }],
  161. [OWSTableItem itemWithTitle:@"Create 100 fake threads with 100 messages"
  162. actionBlock:^{
  163. [DebugUIMessages createFakeThreads:100 withFakeMessages:100];
  164. }],
  165. [OWSTableItem itemWithTitle:@"Create 1k fake threads with 1 message"
  166. actionBlock:^{
  167. [DebugUIMessages createFakeThreads:1000 withFakeMessages:1];
  168. }],
  169. [OWSTableItem itemWithTitle:@"Create 1k fake messages"
  170. actionBlock:^{
  171. [DebugUIMessages sendFakeMessages:1000 thread:thread];
  172. }],
  173. [OWSTableItem itemWithTitle:@"Create 10k fake messages"
  174. actionBlock:^{
  175. [DebugUIMessages sendFakeMessages:10 * 1000 thread:thread];
  176. }],
  177. [OWSTableItem itemWithTitle:@"Create 10k fake text messages"
  178. actionBlock:^{
  179. [DebugUIMessages sendFakeMessages:10 * 1000 thread:thread isTextOnly:YES];
  180. }],
  181. [OWSTableItem itemWithTitle:@"Create 100k fake messages"
  182. actionBlock:^{
  183. [DebugUIMessages sendFakeMessages:100 * 1000 thread:thread];
  184. }],
  185. [OWSTableItem itemWithTitle:@"Create 100k fake text messages"
  186. actionBlock:^{
  187. [DebugUIMessages sendFakeMessages:100 * 1000 thread:thread isTextOnly:YES];
  188. }],
  189. [OWSTableItem itemWithTitle:@"Send text/x-signal-plain"
  190. actionBlock:^{
  191. [DebugUIMessages sendOversizeTextMessage:thread];
  192. }],
  193. [OWSTableItem itemWithTitle:@"Send unknown mimetype"
  194. actionBlock:^{
  195. [DebugUIMessages sendRandomAttachment:thread uti:kUnknownTestAttachmentUTI];
  196. }],
  197. [OWSTableItem itemWithTitle:@"Send pdf"
  198. actionBlock:^{
  199. [DebugUIMessages sendRandomAttachment:thread uti:(NSString *)kUTTypePDF];
  200. }],
  201. [OWSTableItem itemWithTitle:@"Create all system messages"
  202. actionBlock:^{
  203. [DebugUIMessages createSystemMessagesInThread:thread];
  204. }],
  205. [OWSTableItem itemWithTitle:@"Create messages with variety of timestamps"
  206. actionBlock:^{
  207. [DebugUIMessages createTimestampMessagesInThread:thread];
  208. }],
  209. [OWSTableItem itemWithTitle:@"Send 10 text and system messages"
  210. actionBlock:^{
  211. [DebugUIMessages sendTextAndSystemMessages:10 thread:thread];
  212. }],
  213. [OWSTableItem itemWithTitle:@"Send 100 text and system messages"
  214. actionBlock:^{
  215. [DebugUIMessages sendTextAndSystemMessages:100 thread:thread];
  216. }],
  217. [OWSTableItem itemWithTitle:@"Send 1,000 text and system messages"
  218. actionBlock:^{
  219. [DebugUIMessages sendTextAndSystemMessages:1000 thread:thread];
  220. }],
  221. [OWSTableItem
  222. itemWithTitle:@"Request Bogus group info"
  223. actionBlock:^{
  224. OWSLogInfo(@"Requesting bogus group info for thread: %@", thread);
  225. OWSSyncGroupsRequestMessage *syncGroupsRequestMessage = [[OWSSyncGroupsRequestMessage alloc]
  226. initWithThread:thread
  227. groupId:[Randomness generateRandomBytes:kGroupIdLength]];
  228. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
  229. [self.messageSenderJobQueue addMessage:syncGroupsRequestMessage transaction:transaction];
  230. }];
  231. }],
  232. [OWSTableItem itemWithTitle:@"Message with stalled timer"
  233. actionBlock:^{
  234. [DebugUIMessages createDisappearingMessagesWhichFailedToStartInThread:thread];
  235. }],
  236. [OWSTableItem itemWithTitle:@"Inject 10 fake incoming messages"
  237. actionBlock:^{
  238. [DebugUIMessages injectFakeIncomingMessages:10 thread:thread];
  239. }],
  240. [OWSTableItem itemWithTitle:@"Inject 100 fake incoming messages"
  241. actionBlock:^{
  242. [DebugUIMessages injectFakeIncomingMessages:100 thread:thread];
  243. }],
  244. [OWSTableItem itemWithTitle:@"Inject 1,000 fake incoming messages"
  245. actionBlock:^{
  246. [DebugUIMessages injectFakeIncomingMessages:1000 thread:thread];
  247. }],
  248. [OWSTableItem itemWithTitle:@"Test Indic Scripts"
  249. actionBlock:^{
  250. [DebugUIMessages testIndicScriptsInThread:thread];
  251. }],
  252. [OWSTableItem itemWithTitle:@"Test Zalgo"
  253. actionBlock:^{
  254. [DebugUIMessages testZalgoTextInThread:thread];
  255. }],
  256. [OWSTableItem itemWithTitle:@"Test Directional Filenames"
  257. actionBlock:^{
  258. [DebugUIMessages testDirectionalFilenamesInThread:thread];
  259. }],
  260. [OWSTableItem itemWithTitle:@"Test Linkification"
  261. actionBlock:^{
  262. [DebugUIMessages testLinkificationInThread:thread];
  263. }],
  264. ]];
  265. if ([thread isKindOfClass:[TSContactThread class]]) {
  266. TSContactThread *contactThread = (TSContactThread *)thread;
  267. NSString *recipientId = contactThread.contactIdentifier;
  268. [items addObject:[OWSTableItem itemWithTitle:@"Create 10 new groups"
  269. actionBlock:^{
  270. [DebugUIMessages createNewGroups:10 recipientId:recipientId];
  271. }]];
  272. [items addObject:[OWSTableItem itemWithTitle:@"Create 100 new groups"
  273. actionBlock:^{
  274. [DebugUIMessages createNewGroups:100 recipientId:recipientId];
  275. }]];
  276. [items addObject:[OWSTableItem itemWithTitle:@"Create 1,000 new groups"
  277. actionBlock:^{
  278. [DebugUIMessages createNewGroups:1000 recipientId:recipientId];
  279. }]];
  280. }
  281. if ([thread isKindOfClass:[TSGroupThread class]]) {
  282. TSGroupThread *groupThread = (TSGroupThread *)thread;
  283. [items addObject:[OWSTableItem itemWithTitle:@"Send message to all members"
  284. actionBlock:^{
  285. [DebugUIMessages sendMessages:1 toAllMembersOfGroup:groupThread];
  286. }]];
  287. }
  288. #endif
  289. return [OWSTableSection sectionWithTitle:self.name items:items];
  290. }
  291. #ifdef DEBUG
  292. #pragma mark - Dependencies
  293. - (YapDatabaseConnection *)dbConnection
  294. {
  295. return self.class.dbConnection;
  296. }
  297. + (YapDatabaseConnection *)dbConnection
  298. {
  299. return SSKEnvironment.shared.primaryStorage.dbReadWriteConnection;
  300. }
  301. + (SSKMessageSenderJobQueue *)messageSenderJobQueue
  302. {
  303. return SSKEnvironment.shared.messageSenderJobQueue;
  304. }
  305. - (SSKMessageSenderJobQueue *)messageSenderJobQueue
  306. {
  307. return self.class.messageSenderJobQueue;
  308. }
  309. + (void)sendMessages:(NSUInteger)count toAllMembersOfGroup:(TSGroupThread *)groupThread
  310. {
  311. for (NSString *recipientId in groupThread.groupModel.groupMemberIds) {
  312. TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId];
  313. [[self sendTextMessagesActionInThread:contactThread] prepareAndPerformNTimes:count];
  314. }
  315. }
  316. + (void)sendTextMessageInThread:(TSThread *)thread counter:(NSUInteger)counter
  317. {
  318. OWSLogInfo(@"sendTextMessageInThread: %zd", counter);
  319. [DDLog flushLog];
  320. NSString *randomText = [self randomText];
  321. NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
  322. __block TSOutgoingMessage *message;
  323. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  324. message = [ThreadUtil enqueueMessageWithText:text
  325. inThread:thread
  326. quotedReplyModel:nil
  327. linkPreviewDraft:nil
  328. transaction:transaction];
  329. }];
  330. OWSLogError(@"sendTextMessageInThread timestamp: %llu.", message.timestamp);
  331. }
  332. + (void)sendNTextMessagesInThread:(TSThread *)thread
  333. {
  334. [self performActionNTimes:[self sendTextMessagesActionInThread:thread]];
  335. }
  336. + (DebugUIMessagesAction *)sendTextMessagesActionInThread:(TSThread *)thread
  337. {
  338. OWSAssertDebug(thread);
  339. return [DebugUIMessagesSingleAction actionWithLabel:@"Send Text Message"
  340. staggeredActionBlock:^(NSUInteger index,
  341. YapDatabaseReadWriteTransaction *transaction,
  342. ActionSuccessBlock success,
  343. ActionFailureBlock failure) {
  344. dispatch_async(dispatch_get_main_queue(), ^{
  345. [self sendTextMessageInThread:thread counter:index];
  346. // TODO:
  347. success();
  348. });
  349. }];
  350. }
  351. + (void)sendAttachmentWithFilePath:(NSString *)filePath
  352. thread:(TSThread *)thread
  353. label:(NSString *)label
  354. hasCaption:(BOOL)hasCaption
  355. success:(nullable void (^)(void))success
  356. failure:(nullable void (^)(void))failure
  357. {
  358. OWSAssertDebug(filePath);
  359. OWSAssertDebug(thread);
  360. NSString *filename = [filePath lastPathComponent];
  361. NSString *utiType = [MIMETypeUtil utiTypeForFileExtension:filename.pathExtension];
  362. DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithFilePath:filePath shouldDeleteOnDeallocation:NO];
  363. [dataSource setSourceFilename:filename];
  364. SignalAttachment *attachment =
  365. [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType imageQuality:TSImageQualityOriginal];
  366. NSString *messageBody = nil;
  367. if (hasCaption) {
  368. // We want a message body that is "more than one line on all devices,
  369. // using all dynamic type sizes."
  370. NSString *sampleText = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, "
  371. @"consectetur adipiscing elit.";
  372. messageBody = [[label stringByAppendingString:@" "] stringByAppendingString:sampleText];
  373. messageBody = [messageBody stringByAppendingString:@" 🔤"];
  374. }
  375. attachment.captionText = messageBody;
  376. OWSAssertDebug(attachment);
  377. if ([attachment hasError]) {
  378. OWSLogError(@"attachment[%@]: %@", [attachment sourceFilename], [attachment errorName]);
  379. [DDLog flushLog];
  380. }
  381. OWSAssertDebug(![attachment hasError]);
  382. [self sendAttachment:attachment thread:thread messageBody:messageBody];
  383. success();
  384. }
  385. #pragma mark - Infrastructure
  386. + (void)performActionNTimes:(DebugUIMessagesAction *)action
  387. {
  388. OWSAssertIsOnMainThread();
  389. OWSAssertDebug(action);
  390. UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"How many?"
  391. message:nil
  392. preferredStyle:UIAlertControllerStyleActionSheet];
  393. for (NSNumber *countValue in @[
  394. @(1),
  395. @(10),
  396. @(100),
  397. @(1 * 1000),
  398. @(10 * 1000),
  399. ]) {
  400. [alert addAction:[UIAlertAction actionWithTitle:countValue.stringValue
  401. style:UIAlertActionStyleDefault
  402. handler:^(UIAlertAction *ignore) {
  403. [action prepareAndPerformNTimes:countValue.unsignedIntegerValue];
  404. }]];
  405. }
  406. [alert addAction:[OWSAlerts cancelAction]];
  407. UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController];
  408. [fromViewController presentViewController:alert animated:YES completion:nil];
  409. }
  410. #pragma mark - Send Media
  411. + (NSArray<DebugUIMessagesAction *> *)allSendMediaActions:(TSThread *)thread
  412. {
  413. OWSAssertDebug(thread);
  414. NSArray<DebugUIMessagesAction *> *actions = @[
  415. [self sendJpegAction:thread hasCaption:NO],
  416. [self sendJpegAction:thread hasCaption:YES],
  417. [self sendGifAction:thread hasCaption:NO],
  418. [self sendGifAction:thread hasCaption:YES],
  419. [self sendLargeGifAction:thread hasCaption:NO],
  420. [self sendLargeGifAction:thread hasCaption:YES],
  421. [self sendMp3Action:thread hasCaption:NO],
  422. [self sendMp3Action:thread hasCaption:YES],
  423. [self sendMp4Action:thread hasCaption:NO],
  424. [self sendMp4Action:thread hasCaption:YES],
  425. ];
  426. return actions;
  427. }
  428. + (DebugUIMessagesAction *)sendJpegAction:(TSThread *)thread hasCaption:(BOOL)hasCaption
  429. {
  430. OWSAssertDebug(thread);
  431. return [self sendMediaAction:@"Send Jpeg"
  432. hasCaption:hasCaption
  433. fakeAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  434. thread:thread];
  435. }
  436. + (DebugUIMessagesAction *)sendGifAction:(TSThread *)thread hasCaption:(BOOL)hasCaption
  437. {
  438. OWSAssertDebug(thread);
  439. return [self sendMediaAction:@"Send Gif"
  440. hasCaption:hasCaption
  441. fakeAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  442. thread:thread];
  443. }
  444. + (DebugUIMessagesAction *)sendLargeGifAction:(TSThread *)thread hasCaption:(BOOL)hasCaption
  445. {
  446. OWSAssertDebug(thread);
  447. return [self sendMediaAction:@"Send Large Gif"
  448. hasCaption:hasCaption
  449. fakeAssetLoader:[DebugUIMessagesAssetLoader largeGifInstance]
  450. thread:thread];
  451. }
  452. + (DebugUIMessagesAction *)sendMp3Action:(TSThread *)thread hasCaption:(BOOL)hasCaption
  453. {
  454. OWSAssertDebug(thread);
  455. return [self sendMediaAction:@"Send Mp3"
  456. hasCaption:hasCaption
  457. fakeAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  458. thread:thread];
  459. }
  460. + (DebugUIMessagesAction *)sendMp4Action:(TSThread *)thread hasCaption:(BOOL)hasCaption
  461. {
  462. OWSAssertDebug(thread);
  463. return [self sendMediaAction:@"Send Mp4"
  464. hasCaption:hasCaption
  465. fakeAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  466. thread:thread];
  467. }
  468. + (DebugUIMessagesAction *)sendMediaAction:(NSString *)labelParam
  469. hasCaption:(BOOL)hasCaption
  470. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  471. thread:(TSThread *)thread
  472. {
  473. OWSAssertDebug(labelParam.length > 0);
  474. OWSAssertDebug(fakeAssetLoader);
  475. OWSAssertDebug(thread);
  476. NSString *label = labelParam;
  477. if (hasCaption) {
  478. label = [label stringByAppendingString:@" 🔤"];
  479. }
  480. return [DebugUIMessagesSingleAction
  481. actionWithLabel:label
  482. staggeredActionBlock:^(NSUInteger index,
  483. YapDatabaseReadWriteTransaction *transaction,
  484. ActionSuccessBlock success,
  485. ActionFailureBlock failure) {
  486. dispatch_async(dispatch_get_main_queue(), ^{
  487. OWSAssertDebug(fakeAssetLoader.filePath.length > 0);
  488. [self sendAttachmentWithFilePath:fakeAssetLoader.filePath
  489. thread:thread
  490. label:label
  491. hasCaption:hasCaption
  492. success:success
  493. failure:failure];
  494. });
  495. }
  496. prepareBlock:fakeAssetLoader.prepareBlock];
  497. }
  498. + (DebugUIMessagesAction *)sendAllMediaAction:(TSThread *)thread
  499. {
  500. OWSAssertDebug(thread);
  501. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Send Media"
  502. subactions:[self allSendMediaActions:thread]];
  503. }
  504. + (DebugUIMessagesAction *)sendRandomMediaAction:(TSThread *)thread
  505. {
  506. OWSAssertDebug(thread);
  507. return [DebugUIMessagesGroupAction randomGroupActionWithLabel:@"Random Send Media"
  508. subactions:[self allSendMediaActions:thread]];
  509. }
  510. + (void)selectSendMediaAction:(TSThread *)thread
  511. {
  512. OWSAssertDebug(thread);
  513. [self selectActionUI:[self allSendMediaActions:thread] label:@"Select Send Media"];
  514. }
  515. #pragma mark - Fake Outgoing Media
  516. + (DebugUIMessagesAction *)fakeOutgoingJpegAction:(TSThread *)thread
  517. messageState:(TSOutgoingMessageState)messageState
  518. hasCaption:(BOOL)hasCaption
  519. {
  520. OWSAssertDebug(thread);
  521. return [self fakeOutgoingMediaAction:@"Fake Outgoing Jpeg"
  522. messageState:messageState
  523. hasCaption:hasCaption
  524. fakeAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  525. thread:thread];
  526. }
  527. + (DebugUIMessagesAction *)fakeOutgoingGifAction:(TSThread *)thread
  528. messageState:(TSOutgoingMessageState)messageState
  529. hasCaption:(BOOL)hasCaption
  530. {
  531. OWSAssertDebug(thread);
  532. return [self fakeOutgoingMediaAction:@"Fake Outgoing Gif"
  533. messageState:messageState
  534. hasCaption:hasCaption
  535. fakeAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  536. thread:thread];
  537. }
  538. + (DebugUIMessagesAction *)fakeOutgoingLargeGifAction:(TSThread *)thread
  539. messageState:(TSOutgoingMessageState)messageState
  540. hasCaption:(BOOL)hasCaption
  541. {
  542. OWSAssertDebug(thread);
  543. return [self fakeOutgoingMediaAction:@"Fake Outgoing Large Gif"
  544. messageState:messageState
  545. hasCaption:hasCaption
  546. fakeAssetLoader:[DebugUIMessagesAssetLoader largeGifInstance]
  547. thread:thread];
  548. }
  549. + (DebugUIMessagesAction *)fakeOutgoingMp3Action:(TSThread *)thread
  550. messageState:(TSOutgoingMessageState)messageState
  551. hasCaption:(BOOL)hasCaption
  552. {
  553. OWSAssertDebug(thread);
  554. return [self fakeOutgoingMediaAction:@"Fake Outgoing Mp3"
  555. messageState:messageState
  556. hasCaption:hasCaption
  557. fakeAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  558. thread:thread];
  559. }
  560. + (DebugUIMessagesAction *)fakeOutgoingMp4Action:(TSThread *)thread
  561. messageState:(TSOutgoingMessageState)messageState
  562. hasCaption:(BOOL)hasCaption
  563. {
  564. OWSAssertDebug(thread);
  565. return [self fakeOutgoingMediaAction:@"Fake Outgoing Mp4"
  566. messageState:messageState
  567. hasCaption:hasCaption
  568. fakeAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  569. thread:thread];
  570. }
  571. + (DebugUIMessagesAction *)fakeOutgoingCompactPortraitPngAction:(TSThread *)thread
  572. messageState:(TSOutgoingMessageState)messageState
  573. hasCaption:(BOOL)hasCaption
  574. {
  575. OWSAssertDebug(thread);
  576. return [self fakeOutgoingMediaAction:@"Fake Outgoing Portrait Png"
  577. messageState:messageState
  578. hasCaption:hasCaption
  579. fakeAssetLoader:[DebugUIMessagesAssetLoader compactLandscapePngInstance]
  580. thread:thread];
  581. }
  582. + (DebugUIMessagesAction *)fakeOutgoingCompactLandscapePngAction:(TSThread *)thread
  583. messageState:(TSOutgoingMessageState)messageState
  584. hasCaption:(BOOL)hasCaption
  585. {
  586. OWSAssertDebug(thread);
  587. return [self fakeOutgoingMediaAction:@"Fake Outgoing Landscape Png"
  588. messageState:messageState
  589. hasCaption:hasCaption
  590. fakeAssetLoader:[DebugUIMessagesAssetLoader compactPortraitPngInstance]
  591. thread:thread];
  592. }
  593. + (DebugUIMessagesAction *)fakeOutgoingTallPortraitPngAction:(TSThread *)thread
  594. messageState:(TSOutgoingMessageState)messageState
  595. hasCaption:(BOOL)hasCaption
  596. {
  597. OWSAssertDebug(thread);
  598. return [self fakeOutgoingMediaAction:@"Fake Outgoing Tall Portrait Png"
  599. messageState:messageState
  600. hasCaption:hasCaption
  601. fakeAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  602. thread:thread];
  603. }
  604. + (DebugUIMessagesAction *)fakeOutgoingWideLandscapePngAction:(TSThread *)thread
  605. messageState:(TSOutgoingMessageState)messageState
  606. hasCaption:(BOOL)hasCaption
  607. {
  608. OWSAssertDebug(thread);
  609. return [self fakeOutgoingMediaAction:@"Fake Outgoing Wide Landscape Png"
  610. messageState:messageState
  611. hasCaption:hasCaption
  612. fakeAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  613. thread:thread];
  614. }
  615. + (DebugUIMessagesAction *)fakeOutgoingLargePngAction:(TSThread *)thread
  616. messageState:(TSOutgoingMessageState)messageState
  617. hasCaption:(BOOL)hasCaption
  618. {
  619. OWSAssertDebug(thread);
  620. return [self fakeOutgoingMediaAction:@"Fake Outgoing Large Png"
  621. messageState:messageState
  622. hasCaption:hasCaption
  623. fakeAssetLoader:[DebugUIMessagesAssetLoader largePngInstance]
  624. thread:thread];
  625. }
  626. + (DebugUIMessagesAction *)fakeOutgoingTinyPngAction:(TSThread *)thread
  627. messageState:(TSOutgoingMessageState)messageState
  628. hasCaption:(BOOL)hasCaption
  629. {
  630. OWSAssertDebug(thread);
  631. return [self fakeOutgoingMediaAction:@"Fake Outgoing Tiny Png"
  632. messageState:messageState
  633. hasCaption:hasCaption
  634. fakeAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  635. thread:thread];
  636. }
  637. + (DebugUIMessagesAction *)fakeOutgoingPngAction:(TSThread *)thread
  638. actionLabel:(NSString *)actionLabel
  639. imageSize:(CGSize)imageSize
  640. backgroundColor:(UIColor *)backgroundColor
  641. textColor:(UIColor *)textColor
  642. imageLabel:(NSString *)imageLabel
  643. messageState:(TSOutgoingMessageState)messageState
  644. hasCaption:(BOOL)hasCaption
  645. {
  646. OWSAssertDebug(thread);
  647. return [self fakeOutgoingMediaAction:actionLabel
  648. messageState:messageState
  649. hasCaption:hasCaption
  650. fakeAssetLoader:[DebugUIMessagesAssetLoader pngInstanceWithSize:imageSize
  651. backgroundColor:backgroundColor
  652. textColor:textColor
  653. label:imageLabel]
  654. thread:thread];
  655. }
  656. + (DebugUIMessagesAction *)fakeOutgoingTinyPdfAction:(TSThread *)thread
  657. messageState:(TSOutgoingMessageState)messageState
  658. hasCaption:(BOOL)hasCaption
  659. {
  660. OWSAssertDebug(thread);
  661. return [self fakeOutgoingMediaAction:@"Fake Outgoing Tiny Pdf"
  662. messageState:messageState
  663. hasCaption:hasCaption
  664. fakeAssetLoader:[DebugUIMessagesAssetLoader tinyPdfInstance]
  665. thread:thread];
  666. }
  667. + (DebugUIMessagesAction *)fakeOutgoingLargePdfAction:(TSThread *)thread
  668. messageState:(TSOutgoingMessageState)messageState
  669. hasCaption:(BOOL)hasCaption
  670. {
  671. OWSAssertDebug(thread);
  672. return [self fakeOutgoingMediaAction:@"Fake Outgoing Large Pdf"
  673. messageState:messageState
  674. hasCaption:hasCaption
  675. fakeAssetLoader:[DebugUIMessagesAssetLoader largePdfInstance]
  676. thread:thread];
  677. }
  678. + (DebugUIMessagesAction *)fakeOutgoingMissingPngAction:(TSThread *)thread
  679. messageState:(TSOutgoingMessageState)messageState
  680. hasCaption:(BOOL)hasCaption
  681. {
  682. OWSAssertDebug(thread);
  683. return [self fakeOutgoingMediaAction:@"Fake Outgoing Missing Png"
  684. messageState:messageState
  685. hasCaption:hasCaption
  686. fakeAssetLoader:[DebugUIMessagesAssetLoader missingPngInstance]
  687. thread:thread];
  688. }
  689. + (DebugUIMessagesAction *)fakeOutgoingMissingPdfAction:(TSThread *)thread
  690. messageState:(TSOutgoingMessageState)messageState
  691. hasCaption:(BOOL)hasCaption
  692. {
  693. OWSAssertDebug(thread);
  694. return [self fakeOutgoingMediaAction:@"Fake Outgoing Missing Pdf"
  695. messageState:messageState
  696. hasCaption:hasCaption
  697. fakeAssetLoader:[DebugUIMessagesAssetLoader missingPdfInstance]
  698. thread:thread];
  699. }
  700. + (DebugUIMessagesAction *)fakeOutgoingOversizeTextAction:(TSThread *)thread
  701. messageState:(TSOutgoingMessageState)messageState
  702. hasCaption:(BOOL)hasCaption
  703. {
  704. OWSAssertDebug(thread);
  705. return [self fakeOutgoingMediaAction:@"Fake Outgoing Oversize Text"
  706. messageState:messageState
  707. hasCaption:hasCaption
  708. fakeAssetLoader:[DebugUIMessagesAssetLoader oversizeTextInstance]
  709. thread:thread];
  710. }
  711. + (DebugUIMessagesAction *)fakeOutgoingMediaAction:(NSString *)labelParam
  712. messageState:(TSOutgoingMessageState)messageState
  713. hasCaption:(BOOL)hasCaption
  714. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  715. thread:(TSThread *)thread
  716. {
  717. OWSAssertDebug(labelParam.length > 0);
  718. OWSAssertDebug(fakeAssetLoader);
  719. OWSAssertDebug(thread);
  720. NSString *label = [labelParam stringByAppendingString:[self actionLabelForHasCaption:hasCaption
  721. outgoingMessageState:messageState
  722. isDelivered:NO
  723. isRead:NO]];
  724. return
  725. [DebugUIMessagesSingleAction actionWithLabel:label
  726. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  727. OWSAssertDebug(fakeAssetLoader.filePath.length > 0);
  728. [self createFakeOutgoingMedia:index
  729. messageState:messageState
  730. hasCaption:hasCaption
  731. fakeAssetLoader:fakeAssetLoader
  732. thread:thread
  733. transaction:transaction];
  734. }
  735. prepareBlock:fakeAssetLoader.prepareBlock];
  736. }
  737. + (void)createFakeOutgoingMedia:(NSUInteger)index
  738. messageState:(TSOutgoingMessageState)messageState
  739. hasCaption:(BOOL)hasCaption
  740. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  741. thread:(TSThread *)thread
  742. transaction:(YapDatabaseReadWriteTransaction *)transaction
  743. {
  744. OWSAssertDebug(thread);
  745. OWSAssertDebug(fakeAssetLoader.filePath);
  746. OWSAssertDebug(transaction);
  747. // Random time within last n years. Helpful for filling out a media gallery over time.
  748. // double yearsMillis = 4.0 * kYearsInMs;
  749. // uint64_t millisAgo = (uint64_t)(((double)arc4random() / ((double)0xffffffff)) * yearsMillis);
  750. // uint64_t timestamp = [NSDate ows_millisecondTimeStamp] - millisAgo;
  751. uint64_t timestamp = [NSDate ows_millisecondTimeStamp];
  752. NSString *messageBody = nil;
  753. if (hasCaption) {
  754. // We want a message body that is "more than one line on all devices,
  755. // using all dynamic type sizes."
  756. NSString *sampleText = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, "
  757. @"consectetur adipiscing elit.";
  758. messageBody = [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:sampleText];
  759. messageBody = [messageBody stringByAppendingString:[self actionLabelForHasCaption:hasCaption
  760. outgoingMessageState:messageState
  761. isDelivered:NO
  762. isRead:NO]];
  763. }
  764. TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread
  765. messageBody:messageBody
  766. fakeAssetLoader:fakeAssetLoader
  767. messageState:messageState
  768. isDelivered:YES
  769. isRead:NO
  770. quotedMessage:nil
  771. contactShare:nil
  772. linkPreview:nil
  773. transaction:transaction];
  774. // This is a hack to "back-date" the message.
  775. [message setReceivedAtTimestamp:timestamp];
  776. [message saveWithTransaction:transaction];
  777. }
  778. #pragma mark - Fake Incoming Media
  779. + (DebugUIMessagesAction *)fakeIncomingJpegAction:(TSThread *)thread
  780. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  781. hasCaption:(BOOL)hasCaption
  782. {
  783. OWSAssertDebug(thread);
  784. return [self fakeIncomingMediaAction:@"Fake Incoming Jpeg"
  785. isAttachmentDownloaded:isAttachmentDownloaded
  786. hasCaption:hasCaption
  787. fakeAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  788. thread:thread];
  789. }
  790. + (DebugUIMessagesAction *)fakeIncomingGifAction:(TSThread *)thread
  791. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  792. hasCaption:(BOOL)hasCaption
  793. {
  794. OWSAssertDebug(thread);
  795. return [self fakeIncomingMediaAction:@"Fake Incoming Gif"
  796. isAttachmentDownloaded:isAttachmentDownloaded
  797. hasCaption:hasCaption
  798. fakeAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  799. thread:thread];
  800. }
  801. + (DebugUIMessagesAction *)fakeIncomingLargeGifAction:(TSThread *)thread
  802. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  803. hasCaption:(BOOL)hasCaption
  804. {
  805. OWSAssertDebug(thread);
  806. return [self fakeIncomingMediaAction:@"Fake Incoming Large Gif"
  807. isAttachmentDownloaded:isAttachmentDownloaded
  808. hasCaption:hasCaption
  809. fakeAssetLoader:[DebugUIMessagesAssetLoader largeGifInstance]
  810. thread:thread];
  811. }
  812. + (DebugUIMessagesAction *)fakeIncomingMp3Action:(TSThread *)thread
  813. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  814. hasCaption:(BOOL)hasCaption
  815. {
  816. OWSAssertDebug(thread);
  817. return [self fakeIncomingMediaAction:@"Fake Incoming Mp3"
  818. isAttachmentDownloaded:isAttachmentDownloaded
  819. hasCaption:hasCaption
  820. fakeAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  821. thread:thread];
  822. }
  823. + (DebugUIMessagesAction *)fakeIncomingMp4Action:(TSThread *)thread
  824. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  825. hasCaption:(BOOL)hasCaption
  826. {
  827. OWSAssertDebug(thread);
  828. return [self fakeIncomingMediaAction:@"Fake Incoming Mp4"
  829. isAttachmentDownloaded:isAttachmentDownloaded
  830. hasCaption:hasCaption
  831. fakeAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  832. thread:thread];
  833. }
  834. + (DebugUIMessagesAction *)fakeIncomingCompactPortraitPngAction:(TSThread *)thread
  835. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  836. hasCaption:(BOOL)hasCaption
  837. {
  838. OWSAssertDebug(thread);
  839. return [self fakeIncomingMediaAction:@"Fake Incoming Portrait Png"
  840. isAttachmentDownloaded:isAttachmentDownloaded
  841. hasCaption:hasCaption
  842. fakeAssetLoader:[DebugUIMessagesAssetLoader compactPortraitPngInstance]
  843. thread:thread];
  844. }
  845. + (DebugUIMessagesAction *)fakeIncomingCompactLandscapePngAction:(TSThread *)thread
  846. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  847. hasCaption:(BOOL)hasCaption
  848. {
  849. OWSAssertDebug(thread);
  850. return [self fakeIncomingMediaAction:@"Fake Incoming Landscape Png"
  851. isAttachmentDownloaded:isAttachmentDownloaded
  852. hasCaption:hasCaption
  853. fakeAssetLoader:[DebugUIMessagesAssetLoader compactLandscapePngInstance]
  854. thread:thread];
  855. }
  856. + (DebugUIMessagesAction *)fakeIncomingTallPortraitPngAction:(TSThread *)thread
  857. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  858. hasCaption:(BOOL)hasCaption
  859. {
  860. OWSAssertDebug(thread);
  861. return [self fakeIncomingMediaAction:@"Fake Incoming Tall Portrait Png"
  862. isAttachmentDownloaded:isAttachmentDownloaded
  863. hasCaption:hasCaption
  864. fakeAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  865. thread:thread];
  866. }
  867. + (DebugUIMessagesAction *)fakeIncomingWideLandscapePngAction:(TSThread *)thread
  868. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  869. hasCaption:(BOOL)hasCaption
  870. {
  871. OWSAssertDebug(thread);
  872. return [self fakeIncomingMediaAction:@"Fake Incoming Wide Landscape Png"
  873. isAttachmentDownloaded:isAttachmentDownloaded
  874. hasCaption:hasCaption
  875. fakeAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  876. thread:thread];
  877. }
  878. + (DebugUIMessagesAction *)fakeIncomingLargePngAction:(TSThread *)thread
  879. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  880. hasCaption:(BOOL)hasCaption
  881. {
  882. OWSAssertDebug(thread);
  883. return [self fakeIncomingMediaAction:@"Fake Incoming Large Png"
  884. isAttachmentDownloaded:isAttachmentDownloaded
  885. hasCaption:hasCaption
  886. fakeAssetLoader:[DebugUIMessagesAssetLoader largePngInstance]
  887. thread:thread];
  888. }
  889. + (DebugUIMessagesAction *)fakeIncomingTinyPngAction:(TSThread *)thread
  890. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  891. hasCaption:(BOOL)hasCaption
  892. {
  893. OWSAssertDebug(thread);
  894. return [self fakeIncomingMediaAction:@"Tiny Incoming Large Png"
  895. isAttachmentDownloaded:isAttachmentDownloaded
  896. hasCaption:hasCaption
  897. fakeAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  898. thread:thread];
  899. }
  900. + (DebugUIMessagesAction *)fakeIncomingPngAction:(TSThread *)thread
  901. actionLabel:(NSString *)actionLabel
  902. imageSize:(CGSize)imageSize
  903. backgroundColor:(UIColor *)backgroundColor
  904. textColor:(UIColor *)textColor
  905. imageLabel:(NSString *)imageLabel
  906. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  907. hasCaption:(BOOL)hasCaption
  908. {
  909. OWSAssertDebug(thread);
  910. return [self fakeIncomingMediaAction:actionLabel
  911. isAttachmentDownloaded:isAttachmentDownloaded
  912. hasCaption:hasCaption
  913. fakeAssetLoader:[DebugUIMessagesAssetLoader pngInstanceWithSize:imageSize
  914. backgroundColor:backgroundColor
  915. textColor:textColor
  916. label:imageLabel]
  917. thread:thread];
  918. }
  919. + (DebugUIMessagesAction *)fakeIncomingTinyPdfAction:(TSThread *)thread
  920. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  921. hasCaption:(BOOL)hasCaption
  922. {
  923. OWSAssertDebug(thread);
  924. return [self fakeIncomingMediaAction:@"Fake Incoming Tiny Pdf"
  925. isAttachmentDownloaded:isAttachmentDownloaded
  926. hasCaption:hasCaption
  927. fakeAssetLoader:[DebugUIMessagesAssetLoader tinyPdfInstance]
  928. thread:thread];
  929. }
  930. + (DebugUIMessagesAction *)fakeIncomingLargePdfAction:(TSThread *)thread
  931. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  932. hasCaption:(BOOL)hasCaption
  933. {
  934. OWSAssertDebug(thread);
  935. return [self fakeIncomingMediaAction:@"Fake Incoming Large Pdf"
  936. isAttachmentDownloaded:isAttachmentDownloaded
  937. hasCaption:hasCaption
  938. fakeAssetLoader:[DebugUIMessagesAssetLoader largePdfInstance]
  939. thread:thread];
  940. }
  941. + (DebugUIMessagesAction *)fakeIncomingMissingPngAction:(TSThread *)thread
  942. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  943. hasCaption:(BOOL)hasCaption
  944. {
  945. OWSAssertDebug(thread);
  946. return [self fakeIncomingMediaAction:@"Fake Incoming Missing Png"
  947. isAttachmentDownloaded:isAttachmentDownloaded
  948. hasCaption:hasCaption
  949. fakeAssetLoader:[DebugUIMessagesAssetLoader missingPngInstance]
  950. thread:thread];
  951. }
  952. + (DebugUIMessagesAction *)fakeIncomingMissingPdfAction:(TSThread *)thread
  953. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  954. hasCaption:(BOOL)hasCaption
  955. {
  956. OWSAssertDebug(thread);
  957. return [self fakeIncomingMediaAction:@"Fake Incoming Missing Pdf"
  958. isAttachmentDownloaded:isAttachmentDownloaded
  959. hasCaption:hasCaption
  960. fakeAssetLoader:[DebugUIMessagesAssetLoader missingPdfInstance]
  961. thread:thread];
  962. }
  963. + (DebugUIMessagesAction *)fakeIncomingOversizeTextAction:(TSThread *)thread
  964. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  965. hasCaption:(BOOL)hasCaption
  966. {
  967. OWSAssertDebug(thread);
  968. return [self fakeIncomingMediaAction:@"Fake Incoming Oversize Text"
  969. isAttachmentDownloaded:isAttachmentDownloaded
  970. hasCaption:hasCaption
  971. fakeAssetLoader:[DebugUIMessagesAssetLoader oversizeTextInstance]
  972. thread:thread];
  973. }
  974. + (DebugUIMessagesAction *)fakeIncomingMediaAction:(NSString *)labelParam
  975. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  976. hasCaption:(BOOL)hasCaption
  977. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  978. thread:(TSThread *)thread
  979. {
  980. OWSAssertDebug(labelParam.length > 0);
  981. OWSAssertDebug(fakeAssetLoader);
  982. OWSAssertDebug(thread);
  983. NSString *label = labelParam;
  984. if (hasCaption) {
  985. label = [label stringByAppendingString:@" 🔤"];
  986. }
  987. if (isAttachmentDownloaded) {
  988. label = [label stringByAppendingString:@" 👍"];
  989. }
  990. return
  991. [DebugUIMessagesSingleAction actionWithLabel:label
  992. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  993. OWSAssertDebug(fakeAssetLoader.filePath.length > 0);
  994. [self createFakeIncomingMedia:index
  995. isAttachmentDownloaded:isAttachmentDownloaded
  996. hasCaption:hasCaption
  997. fakeAssetLoader:fakeAssetLoader
  998. thread:thread
  999. transaction:transaction];
  1000. }
  1001. prepareBlock:fakeAssetLoader.prepareBlock];
  1002. }
  1003. + (TSIncomingMessage *)createFakeIncomingMedia:(NSUInteger)index
  1004. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  1005. hasCaption:(BOOL)hasCaption
  1006. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  1007. thread:(TSThread *)thread
  1008. transaction:(YapDatabaseReadWriteTransaction *)transaction
  1009. {
  1010. NSString *_Nullable caption = nil;
  1011. if (hasCaption) {
  1012. // We want a message body that is "more than one line on all devices,
  1013. // using all dynamic type sizes."
  1014. caption = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, "
  1015. @"consectetur adipiscing elit.";
  1016. }
  1017. return [self createFakeIncomingMedia:index
  1018. isAttachmentDownloaded:isAttachmentDownloaded
  1019. caption:caption
  1020. fakeAssetLoader:fakeAssetLoader
  1021. thread:thread
  1022. transaction:transaction];
  1023. }
  1024. + (TSIncomingMessage *)createFakeIncomingMedia:(NSUInteger)index
  1025. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  1026. caption:(nullable NSString *)caption
  1027. fakeAssetLoader:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  1028. thread:(TSThread *)thread
  1029. transaction:(YapDatabaseReadWriteTransaction *)transaction
  1030. {
  1031. OWSAssertDebug(thread);
  1032. OWSAssertDebug(fakeAssetLoader.filePath);
  1033. OWSAssertDebug(transaction);
  1034. // // Random time within last n years. Helpful for filling out a media gallery over time.
  1035. // double yearsMillis = 4.0 * kYearsInMs;
  1036. // uint64_t millisAgo = (uint64_t)(((double)arc4random() / ((double)0xffffffff)) * yearsMillis);
  1037. // uint64_t timestamp = [NSDate ows_millisecondTimeStamp] - millisAgo;
  1038. NSString *messageBody = nil;
  1039. if (caption) {
  1040. messageBody = [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:caption];
  1041. messageBody = [messageBody stringByAppendingString:@" 🔤"];
  1042. if (isAttachmentDownloaded) {
  1043. messageBody = [messageBody stringByAppendingString:@" 👍"];
  1044. }
  1045. }
  1046. return [self createFakeIncomingMessage:thread
  1047. messageBody:messageBody
  1048. fakeAssetLoader:fakeAssetLoader
  1049. isAttachmentDownloaded:isAttachmentDownloaded
  1050. quotedMessage:nil
  1051. transaction:transaction];
  1052. }
  1053. #pragma mark - Fake Media
  1054. + (NSArray<DebugUIMessagesAction *> *)allFakeMediaActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  1055. {
  1056. OWSAssertDebug(thread);
  1057. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  1058. if (includeLabels) {
  1059. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1060. messageState:TSOutgoingMessageStateSent
  1061. text:@"⚠️ Outgoing Jpeg ⚠️"]];
  1062. }
  1063. [actions addObjectsFromArray:@[
  1064. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1065. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1066. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1067. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1068. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1069. [self fakeOutgoingJpegAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1070. ]];
  1071. if (includeLabels) {
  1072. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1073. messageState:TSOutgoingMessageStateSent
  1074. text:@"⚠️ Outgoing Gif ⚠️"]];
  1075. }
  1076. [actions addObjectsFromArray:@[
  1077. // Don't bother with multiple GIF states.
  1078. [self fakeOutgoingGifAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1079. [self fakeOutgoingLargeGifAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1080. ]];
  1081. if (includeLabels) {
  1082. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1083. messageState:TSOutgoingMessageStateSent
  1084. text:@"⚠️ Outgoing Mp3 ⚠️"]];
  1085. }
  1086. [actions addObjectsFromArray:@[
  1087. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1088. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1089. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1090. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1091. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1092. [self fakeOutgoingMp3Action:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1093. ]];
  1094. if (includeLabels) {
  1095. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1096. messageState:TSOutgoingMessageStateSent
  1097. text:@"⚠️ Outgoing Mp4 ⚠️"]];
  1098. }
  1099. [actions addObjectsFromArray:@[
  1100. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1101. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1102. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1103. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1104. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1105. [self fakeOutgoingMp4Action:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1106. ]];
  1107. if (includeLabels) {
  1108. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1109. messageState:TSOutgoingMessageStateSent
  1110. text:@"⚠️ Outgoing Compact Landscape Png ⚠️"]];
  1111. }
  1112. [actions addObjectsFromArray:@[
  1113. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1114. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1115. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1116. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1117. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1118. [self fakeOutgoingCompactLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1119. ]];
  1120. if (includeLabels) {
  1121. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1122. messageState:TSOutgoingMessageStateSent
  1123. text:@"⚠️ Outgoing Compact Portrait Png ⚠️"]];
  1124. }
  1125. [actions addObjectsFromArray:@[
  1126. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1127. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1128. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1129. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1130. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1131. [self fakeOutgoingCompactPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1132. ]];
  1133. if (includeLabels) {
  1134. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1135. messageState:TSOutgoingMessageStateSent
  1136. text:@"⚠️ Outgoing Wide Landscape Png ⚠️"]];
  1137. }
  1138. [actions addObjectsFromArray:@[
  1139. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1140. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1141. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1142. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1143. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1144. [self fakeOutgoingWideLandscapePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1145. ]];
  1146. if (includeLabels) {
  1147. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1148. messageState:TSOutgoingMessageStateSent
  1149. text:@"⚠️ Outgoing Tall Portrait Png ⚠️"]];
  1150. }
  1151. [actions addObjectsFromArray:@[
  1152. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1153. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1154. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1155. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1156. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1157. [self fakeOutgoingTallPortraitPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1158. ]];
  1159. if (includeLabels) {
  1160. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1161. messageState:TSOutgoingMessageStateSent
  1162. text:@"⚠️ Outgoing Large Png ⚠️"]];
  1163. }
  1164. [actions addObjectsFromArray:@[
  1165. [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1166. [self fakeOutgoingLargePngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1167. ]];
  1168. if (includeLabels) {
  1169. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1170. messageState:TSOutgoingMessageStateSent
  1171. text:@"⚠️ Outgoing Tiny Png ⚠️"]];
  1172. }
  1173. [actions addObjectsFromArray:@[
  1174. [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1175. [self fakeOutgoingTinyPngAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1176. ]];
  1177. if (includeLabels) {
  1178. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1179. messageState:TSOutgoingMessageStateSent
  1180. text:@"⚠️ Outgoing Reserved Color Png ⚠️"]];
  1181. }
  1182. ConversationStyle *conversationStyle = [[ConversationStyle alloc] initWithThread:thread];
  1183. [actions addObjectsFromArray:@[
  1184. [self fakeOutgoingPngAction:thread
  1185. actionLabel:@"Fake Outgoing White Png"
  1186. imageSize:CGSizeMake(200.f, 200.f)
  1187. backgroundColor:[UIColor whiteColor]
  1188. textColor:[UIColor ows_signalBrandBlueColor]
  1189. imageLabel:@"W"
  1190. messageState:TSOutgoingMessageStateFailed
  1191. hasCaption:YES],
  1192. [self fakeOutgoingPngAction:thread
  1193. actionLabel:@"Fake Outgoing White Png"
  1194. imageSize:CGSizeMake(200.f, 200.f)
  1195. backgroundColor:[UIColor whiteColor]
  1196. textColor:[UIColor ows_signalBrandBlueColor]
  1197. imageLabel:@"W"
  1198. messageState:TSOutgoingMessageStateSending
  1199. hasCaption:YES],
  1200. [self fakeOutgoingPngAction:thread
  1201. actionLabel:@"Fake Outgoing White Png"
  1202. imageSize:CGSizeMake(200.f, 200.f)
  1203. backgroundColor:[UIColor whiteColor]
  1204. textColor:[UIColor ows_signalBrandBlueColor]
  1205. imageLabel:@"W"
  1206. messageState:TSOutgoingMessageStateSent
  1207. hasCaption:YES],
  1208. [self fakeOutgoingPngAction:thread
  1209. actionLabel:@"Fake Outgoing 'Outgoing' Png"
  1210. imageSize:CGSizeMake(200.f, 200.f)
  1211. backgroundColor:[conversationStyle bubbleColorWithIsIncoming:NO]
  1212. textColor:[UIColor whiteColor]
  1213. imageLabel:@"W"
  1214. messageState:TSOutgoingMessageStateFailed
  1215. hasCaption:YES],
  1216. [self fakeOutgoingPngAction:thread
  1217. actionLabel:@"Fake Outgoing 'Outgoing' Png"
  1218. imageSize:CGSizeMake(200.f, 200.f)
  1219. backgroundColor:[conversationStyle bubbleColorWithIsIncoming:NO]
  1220. textColor:[UIColor whiteColor]
  1221. imageLabel:@"W"
  1222. messageState:TSOutgoingMessageStateSending
  1223. hasCaption:YES],
  1224. [self fakeOutgoingPngAction:thread
  1225. actionLabel:@"Fake Outgoing 'Outgoing' Png"
  1226. imageSize:CGSizeMake(200.f, 200.f)
  1227. backgroundColor:[conversationStyle bubbleColorWithIsIncoming:NO]
  1228. textColor:[UIColor whiteColor]
  1229. imageLabel:@"W"
  1230. messageState:TSOutgoingMessageStateSent
  1231. hasCaption:YES],
  1232. ]];
  1233. if (includeLabels) {
  1234. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1235. messageState:TSOutgoingMessageStateSent
  1236. text:@"⚠️ Outgoing Tiny Pdf ⚠️"]];
  1237. }
  1238. [actions addObjectsFromArray:@[
  1239. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1240. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSending hasCaption:YES],
  1241. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1242. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:YES],
  1243. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1244. [self fakeOutgoingTinyPdfAction:thread messageState:TSOutgoingMessageStateSent hasCaption:YES],
  1245. ]];
  1246. if (includeLabels) {
  1247. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1248. messageState:TSOutgoingMessageStateSent
  1249. text:@"⚠️ Outgoing Large Pdf ⚠️"]];
  1250. }
  1251. [actions addObjectsFromArray:@[
  1252. [self fakeOutgoingLargePdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1253. ]];
  1254. if (includeLabels) {
  1255. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1256. messageState:TSOutgoingMessageStateSent
  1257. text:@"⚠️ Outgoing Missing Png ⚠️"]];
  1258. }
  1259. [actions addObjectsFromArray:@[
  1260. [self fakeOutgoingMissingPngAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1261. ]];
  1262. if (includeLabels) {
  1263. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1264. messageState:TSOutgoingMessageStateSent
  1265. text:@"⚠️ Outgoing Large Pdf ⚠️"]];
  1266. }
  1267. [actions addObjectsFromArray:@[
  1268. [self fakeOutgoingMissingPdfAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1269. ]];
  1270. if (includeLabels) {
  1271. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1272. messageState:TSOutgoingMessageStateSent
  1273. text:@"⚠️ Outgoing Oversize Text ⚠️"]];
  1274. }
  1275. [actions addObjectsFromArray:@[
  1276. [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateFailed hasCaption:NO],
  1277. [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateSending hasCaption:NO],
  1278. [self fakeOutgoingOversizeTextAction:thread messageState:TSOutgoingMessageStateSent hasCaption:NO],
  1279. ]];
  1280. // Incoming
  1281. if (includeLabels) {
  1282. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Jpg ⚠️"]];
  1283. }
  1284. [actions addObjectsFromArray:@[
  1285. [self fakeIncomingJpegAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1286. [self fakeIncomingJpegAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1287. [self fakeIncomingJpegAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1288. [self fakeIncomingJpegAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1289. ]];
  1290. if (includeLabels) {
  1291. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Gif ⚠️"]];
  1292. }
  1293. [actions addObjectsFromArray:@[
  1294. [self fakeIncomingGifAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1295. [self fakeIncomingLargeGifAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1296. ]];
  1297. if (includeLabels) {
  1298. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Mp3 ⚠️"]];
  1299. }
  1300. [actions addObjectsFromArray:@[
  1301. [self fakeIncomingMp3Action:thread isAttachmentDownloaded:NO hasCaption:NO],
  1302. [self fakeIncomingMp3Action:thread isAttachmentDownloaded:YES hasCaption:NO],
  1303. [self fakeIncomingMp3Action:thread isAttachmentDownloaded:NO hasCaption:YES],
  1304. [self fakeIncomingMp3Action:thread isAttachmentDownloaded:YES hasCaption:YES],
  1305. ]];
  1306. if (includeLabels) {
  1307. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Mp4 ⚠️"]];
  1308. }
  1309. [actions addObjectsFromArray:@[
  1310. [self fakeIncomingMp4Action:thread isAttachmentDownloaded:NO hasCaption:NO],
  1311. [self fakeIncomingMp4Action:thread isAttachmentDownloaded:YES hasCaption:NO],
  1312. [self fakeIncomingMp4Action:thread isAttachmentDownloaded:NO hasCaption:YES],
  1313. [self fakeIncomingMp4Action:thread isAttachmentDownloaded:YES hasCaption:YES],
  1314. ]];
  1315. if (includeLabels) {
  1316. [actions
  1317. addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Compact Landscape Png ⚠️"]];
  1318. }
  1319. [actions addObjectsFromArray:@[
  1320. [self fakeIncomingCompactLandscapePngAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1321. [self fakeIncomingCompactLandscapePngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1322. [self fakeIncomingCompactLandscapePngAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1323. [self fakeIncomingCompactLandscapePngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1324. ]];
  1325. if (includeLabels) {
  1326. [actions
  1327. addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Compact Portrait Png ⚠️"]];
  1328. }
  1329. [actions addObjectsFromArray:@[
  1330. [self fakeIncomingCompactPortraitPngAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1331. [self fakeIncomingCompactPortraitPngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1332. [self fakeIncomingCompactPortraitPngAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1333. [self fakeIncomingCompactPortraitPngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1334. ]];
  1335. if (includeLabels) {
  1336. [actions
  1337. addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Wide Landscape Png ⚠️"]];
  1338. }
  1339. [actions addObjectsFromArray:@[
  1340. [self fakeIncomingWideLandscapePngAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1341. [self fakeIncomingWideLandscapePngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1342. [self fakeIncomingWideLandscapePngAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1343. [self fakeIncomingWideLandscapePngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1344. ]];
  1345. if (includeLabels) {
  1346. [actions
  1347. addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Tall Portrait Png ⚠️"]];
  1348. }
  1349. [actions addObjectsFromArray:@[
  1350. [self fakeIncomingTallPortraitPngAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1351. [self fakeIncomingTallPortraitPngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1352. [self fakeIncomingTallPortraitPngAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1353. [self fakeIncomingTallPortraitPngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1354. ]];
  1355. if (includeLabels) {
  1356. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Large Png ⚠️"]];
  1357. }
  1358. [actions addObjectsFromArray:@[
  1359. [self fakeIncomingLargePngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1360. [self fakeIncomingLargePngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1361. ]];
  1362. if (includeLabels) {
  1363. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Tiny Png ⚠️"]];
  1364. }
  1365. [actions addObjectsFromArray:@[
  1366. [self fakeIncomingTinyPngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1367. [self fakeIncomingTinyPngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1368. ]];
  1369. if (includeLabels) {
  1370. [actions
  1371. addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Reserved Color Png ⚠️"]];
  1372. }
  1373. [actions addObjectsFromArray:@[
  1374. [self fakeIncomingPngAction:thread
  1375. actionLabel:@"Fake Incoming White Png"
  1376. imageSize:CGSizeMake(200.f, 200.f)
  1377. backgroundColor:[UIColor whiteColor]
  1378. textColor:[UIColor ows_signalBrandBlueColor]
  1379. imageLabel:@"W"
  1380. isAttachmentDownloaded:YES
  1381. hasCaption:YES],
  1382. [self fakeIncomingPngAction:thread
  1383. actionLabel:@"Fake Incoming White Png"
  1384. imageSize:CGSizeMake(200.f, 200.f)
  1385. backgroundColor:[UIColor whiteColor]
  1386. textColor:[UIColor ows_signalBrandBlueColor]
  1387. imageLabel:@"W"
  1388. isAttachmentDownloaded:NO
  1389. hasCaption:YES],
  1390. [self fakeIncomingPngAction:thread
  1391. actionLabel:@"Fake Incoming 'Incoming' Png"
  1392. imageSize:CGSizeMake(200.f, 200.f)
  1393. backgroundColor:[conversationStyle conversationColor].primaryColor
  1394. textColor:[UIColor whiteColor]
  1395. imageLabel:@"W"
  1396. isAttachmentDownloaded:YES
  1397. hasCaption:YES],
  1398. [self fakeIncomingPngAction:thread
  1399. actionLabel:@"Fake Incoming 'Incoming' Png"
  1400. imageSize:CGSizeMake(200.f, 200.f)
  1401. backgroundColor:[conversationStyle conversationColor].shadeColor
  1402. textColor:[UIColor whiteColor]
  1403. imageLabel:@"W"
  1404. isAttachmentDownloaded:YES
  1405. hasCaption:YES],
  1406. [self fakeIncomingPngAction:thread
  1407. actionLabel:@"Fake Incoming 'Incoming' Png"
  1408. imageSize:CGSizeMake(200.f, 200.f)
  1409. backgroundColor:[conversationStyle conversationColor].primaryColor
  1410. textColor:[UIColor whiteColor]
  1411. imageLabel:@"W"
  1412. isAttachmentDownloaded:NO
  1413. hasCaption:YES],
  1414. [self fakeIncomingPngAction:thread
  1415. actionLabel:@"Fake Incoming 'Incoming' Png"
  1416. imageSize:CGSizeMake(200.f, 200.f)
  1417. backgroundColor:[conversationStyle conversationColor].shadeColor
  1418. textColor:[UIColor whiteColor]
  1419. imageLabel:@"W"
  1420. isAttachmentDownloaded:NO
  1421. hasCaption:YES],
  1422. ]];
  1423. if (includeLabels) {
  1424. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Tiny Pdf ⚠️"]];
  1425. }
  1426. [actions addObjectsFromArray:@[
  1427. [self fakeIncomingTinyPdfAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1428. [self fakeIncomingTinyPdfAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1429. [self fakeIncomingTinyPdfAction:thread isAttachmentDownloaded:NO hasCaption:YES],
  1430. [self fakeIncomingTinyPdfAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1431. ]];
  1432. if (includeLabels) {
  1433. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Large Pdf ⚠️"]];
  1434. }
  1435. [actions addObjectsFromArray:@[
  1436. [self fakeIncomingLargePdfAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1437. ]];
  1438. if (includeLabels) {
  1439. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Missing Png ⚠️"]];
  1440. }
  1441. [actions addObjectsFromArray:@[
  1442. [self fakeIncomingMissingPngAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1443. [self fakeIncomingMissingPngAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1444. ]];
  1445. if (includeLabels) {
  1446. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Missing Pdf ⚠️"]];
  1447. }
  1448. [actions addObjectsFromArray:@[
  1449. [self fakeIncomingMissingPdfAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1450. [self fakeIncomingMissingPdfAction:thread isAttachmentDownloaded:YES hasCaption:YES],
  1451. ]];
  1452. if (includeLabels) {
  1453. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Oversize Text ⚠️"]];
  1454. }
  1455. [actions addObjectsFromArray:@[
  1456. [self fakeIncomingOversizeTextAction:thread isAttachmentDownloaded:NO hasCaption:NO],
  1457. [self fakeIncomingOversizeTextAction:thread isAttachmentDownloaded:YES hasCaption:NO],
  1458. ]];
  1459. return actions;
  1460. }
  1461. + (DebugUIMessagesAction *)fakeAllMediaAction:(TSThread *)thread
  1462. {
  1463. OWSAssertDebug(thread);
  1464. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Media"
  1465. subactions:[self allFakeMediaActions:thread includeLabels:YES]];
  1466. }
  1467. + (DebugUIMessagesAction *)fakeRandomMediaAction:(TSThread *)thread
  1468. {
  1469. OWSAssertDebug(thread);
  1470. return [DebugUIMessagesGroupAction randomGroupActionWithLabel:@"Random Fake Media"
  1471. subactions:[self allFakeMediaActions:thread includeLabels:NO]];
  1472. }
  1473. #pragma mark - Send Text Messages
  1474. + (DebugUIMessagesAction *)sendShortTextMessageAction:(TSThread *)thread
  1475. {
  1476. OWSAssertDebug(thread);
  1477. return [DebugUIMessagesSingleAction actionWithLabel:@"Send Short Text Message"
  1478. staggeredActionBlock:^(NSUInteger index,
  1479. YapDatabaseReadWriteTransaction *transaction,
  1480. ActionSuccessBlock success,
  1481. ActionFailureBlock failure) {
  1482. dispatch_async(dispatch_get_main_queue(), ^{
  1483. [self sendTextMessageInThread:thread counter:index];
  1484. });
  1485. }];
  1486. }
  1487. + (DebugUIMessagesAction *)sendOversizeTextMessageAction:(TSThread *)thread
  1488. {
  1489. OWSAssertDebug(thread);
  1490. return [DebugUIMessagesSingleAction actionWithLabel:@"Send Oversize Text Message"
  1491. staggeredActionBlock:^(NSUInteger index,
  1492. YapDatabaseReadWriteTransaction *transaction,
  1493. ActionSuccessBlock success,
  1494. ActionFailureBlock failure) {
  1495. dispatch_async(dispatch_get_main_queue(), ^{
  1496. [self sendOversizeTextMessage:thread];
  1497. });
  1498. }];
  1499. }
  1500. + (DebugUIMessagesAction *)sendMessageVariationsAction:(TSThread *)thread
  1501. {
  1502. OWSAssertDebug(thread);
  1503. NSArray<DebugUIMessagesAction *> *actions = @[
  1504. [self sendShortTextMessageAction:thread],
  1505. [self sendOversizeTextMessageAction:thread],
  1506. ];
  1507. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"Send Conversation Cell Variations" subactions:actions];
  1508. }
  1509. #pragma mark - Fake Text Messages
  1510. + (DebugUIMessagesAction *)fakeShortIncomingTextMessageAction:(TSThread *)thread
  1511. {
  1512. OWSAssertDebug(thread);
  1513. return [DebugUIMessagesSingleAction
  1514. actionWithLabel:@"Fake Short Incoming Text Message"
  1515. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  1516. NSString *messageBody =
  1517. [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:[self randomText]];
  1518. [self createFakeIncomingMessage:thread
  1519. messageBody:messageBody
  1520. fakeAssetLoader:nil
  1521. isAttachmentDownloaded:NO
  1522. quotedMessage:nil
  1523. transaction:transaction];
  1524. }];
  1525. }
  1526. + (SignalAttachment *)signalAttachmentForFilePath:(NSString *)filePath
  1527. {
  1528. OWSAssertDebug(filePath);
  1529. NSString *filename = [filePath lastPathComponent];
  1530. NSString *utiType = [MIMETypeUtil utiTypeForFileExtension:filename.pathExtension];
  1531. DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithFilePath:filePath shouldDeleteOnDeallocation:NO];
  1532. [dataSource setSourceFilename:filename];
  1533. SignalAttachment *attachment =
  1534. [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType imageQuality:TSImageQualityOriginal];
  1535. if (arc4random_uniform(100) > 50) {
  1536. attachment.captionText = [self randomCaptionText];
  1537. }
  1538. OWSAssertDebug(attachment);
  1539. if ([attachment hasError]) {
  1540. OWSLogError(@"attachment[%@]: %@", [attachment sourceFilename], [attachment errorName]);
  1541. [DDLog flushLog];
  1542. }
  1543. OWSAssertDebug(![attachment hasError]);
  1544. return attachment;
  1545. }
  1546. + (void)sendAttachment:(nullable SignalAttachment *)attachment
  1547. thread:(TSThread *)thread
  1548. messageBody:(nullable NSString *)messageBody
  1549. {
  1550. [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
  1551. NSArray<SignalAttachment *> *attachments = @[];
  1552. if (attachment != nil) {
  1553. attachments = @[ attachment ];
  1554. }
  1555. [ThreadUtil enqueueMessageWithText:messageBody
  1556. mediaAttachments:attachments
  1557. inThread:thread
  1558. quotedReplyModel:nil
  1559. linkPreviewDraft:nil
  1560. transaction:transaction];
  1561. }];
  1562. }
  1563. + (DebugUIMessagesAction *)fakeIncomingTextMessageAction:(TSThread *)thread text:(NSString *)text
  1564. {
  1565. OWSAssertDebug(thread);
  1566. return [DebugUIMessagesSingleAction
  1567. actionWithLabel:[NSString stringWithFormat:@"Fake Incoming Text Message (%@)", text]
  1568. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  1569. NSString *messageBody = [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:text];
  1570. [self createFakeIncomingMessage:thread
  1571. messageBody:messageBody
  1572. fakeAssetLoader:nil
  1573. isAttachmentDownloaded:NO
  1574. quotedMessage:nil
  1575. transaction:transaction];
  1576. }];
  1577. }
  1578. + (DebugUIMessagesAction *)fakeOutgoingTextMessageAction:(TSThread *)thread
  1579. messageState:(TSOutgoingMessageState)messageState
  1580. text:(NSString *)text
  1581. {
  1582. OWSAssertDebug(thread);
  1583. return [DebugUIMessagesSingleAction
  1584. actionWithLabel:[NSString stringWithFormat:@"Fake Incoming Text Message (%@)", text]
  1585. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  1586. NSString *messageBody = [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:text];
  1587. [self createFakeOutgoingMessage:thread
  1588. messageBody:messageBody
  1589. fakeAssetLoader:nil
  1590. messageState:messageState
  1591. isDelivered:NO
  1592. isRead:NO
  1593. quotedMessage:nil
  1594. contactShare:nil
  1595. linkPreview:nil
  1596. transaction:transaction];
  1597. }];
  1598. }
  1599. + (DebugUIMessagesAction *)fakeShortOutgoingTextMessageAction:(TSThread *)thread
  1600. messageState:(TSOutgoingMessageState)messageState
  1601. {
  1602. return [self fakeShortOutgoingTextMessageAction:thread messageState:messageState isDelivered:NO isRead:NO];
  1603. }
  1604. + (DebugUIMessagesAction *)fakeShortOutgoingTextMessageAction:(TSThread *)thread
  1605. messageState:(TSOutgoingMessageState)messageState
  1606. isDelivered:(BOOL)isDelivered
  1607. isRead:(BOOL)isRead
  1608. {
  1609. return [self fakeShortOutgoingTextMessageAction:(TSThread *)thread
  1610. text:[self randomText]
  1611. messageState:messageState
  1612. isDelivered:isDelivered
  1613. isRead:isRead];
  1614. }
  1615. + (DebugUIMessagesAction *)fakeShortOutgoingTextMessageAction:(TSThread *)thread
  1616. text:(NSString *)text
  1617. messageState:(TSOutgoingMessageState)messageState
  1618. isDelivered:(BOOL)isDelivered
  1619. isRead:(BOOL)isRead
  1620. {
  1621. OWSAssertDebug(thread);
  1622. NSString *label = @"Fake Short Incoming Text Message";
  1623. label = [label stringByAppendingString:[self actionLabelForHasCaption:YES
  1624. outgoingMessageState:messageState
  1625. isDelivered:isDelivered
  1626. isRead:isRead]];
  1627. return [DebugUIMessagesSingleAction
  1628. actionWithLabel:label
  1629. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  1630. NSString *messageBody = [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:text];
  1631. [self createFakeOutgoingMessage:thread
  1632. messageBody:messageBody
  1633. fakeAssetLoader:nil
  1634. messageState:messageState
  1635. isDelivered:isDelivered
  1636. isRead:isRead
  1637. quotedMessage:nil
  1638. contactShare:nil
  1639. linkPreview:nil
  1640. transaction:transaction];
  1641. }];
  1642. }
  1643. + (NSArray<DebugUIMessagesAction *> *)allFakeTextActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  1644. {
  1645. OWSAssertDebug(thread);
  1646. NSArray<NSString *> *messageBodies = @[
  1647. @"Hi",
  1648. @"1️⃣",
  1649. @"1️⃣2️⃣",
  1650. @"1️⃣2️⃣3️⃣",
  1651. @"落",
  1652. @"﷽",
  1653. ];
  1654. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  1655. if (includeLabels) {
  1656. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"⚠️ Incoming Message Bodies ⚠️"]];
  1657. }
  1658. [actions addObject:[self fakeShortIncomingTextMessageAction:thread]];
  1659. for (NSString *messageBody in messageBodies) {
  1660. [actions addObject:[self fakeIncomingTextMessageAction:thread text:messageBody]];
  1661. }
  1662. if (includeLabels) {
  1663. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1664. messageState:TSOutgoingMessageStateSent
  1665. text:@"⚠️ Outgoing Statuses ⚠️"]];
  1666. }
  1667. [actions addObjectsFromArray:@[
  1668. [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateFailed],
  1669. [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSending],
  1670. [self fakeShortOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSent],
  1671. [self fakeShortOutgoingTextMessageAction:thread
  1672. messageState:TSOutgoingMessageStateSent
  1673. isDelivered:YES
  1674. isRead:NO],
  1675. [self fakeShortOutgoingTextMessageAction:thread
  1676. messageState:TSOutgoingMessageStateSent
  1677. isDelivered:YES
  1678. isRead:YES],
  1679. ]];
  1680. if (includeLabels) {
  1681. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1682. messageState:TSOutgoingMessageStateSent
  1683. text:@"⚠️ Outgoing Message Bodies ⚠️"]];
  1684. }
  1685. for (NSString *messageBody in messageBodies) {
  1686. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  1687. messageState:TSOutgoingMessageStateSent
  1688. text:messageBody]];
  1689. }
  1690. return actions;
  1691. }
  1692. + (DebugUIMessagesAction *)fakeAllTextAction:(TSThread *)thread
  1693. {
  1694. OWSAssertDebug(thread);
  1695. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Text"
  1696. subactions:[self allFakeTextActions:thread includeLabels:YES]];
  1697. }
  1698. + (DebugUIMessagesAction *)fakeRandomTextAction:(TSThread *)thread
  1699. {
  1700. OWSAssertDebug(thread);
  1701. return [DebugUIMessagesGroupAction randomGroupActionWithLabel:@"Random Fake Text"
  1702. subactions:[self allFakeTextActions:thread includeLabels:NO]];
  1703. }
  1704. #pragma mark - Fake Quoted Replies
  1705. + (DebugUIMessagesAction *)
  1706. fakeQuotedReplyAction:(TSThread *)thread
  1707. quotedMessageLabel:(NSString *)quotedMessageLabel
  1708. isQuotedMessageIncoming:(BOOL)isQuotedMessageIncoming
  1709. // Optional. At least one of quotedMessageBody and quotedMessageAssetLoader should be non-nil.
  1710. quotedMessageBody:(nullable NSString *)quotedMessageBody
  1711. // Optional. At least one of quotedMessageBody and quotedMessageAssetLoader should be non-nil.
  1712. quotedMessageAssetLoader:(nullable DebugUIMessagesAssetLoader *)quotedMessageAssetLoader
  1713. replyLabel:(NSString *)replyLabel
  1714. isReplyIncoming:(BOOL)isReplyIncoming
  1715. replyMessageBody:(nullable NSString *)replyMessageBody
  1716. replyAssetLoader:(nullable DebugUIMessagesAssetLoader *)replyAssetLoader
  1717. // Only applies if !isReplyIncoming.
  1718. replyMessageState:(TSOutgoingMessageState)replyMessageState
  1719. {
  1720. OWSAssertDebug(thread);
  1721. // Used fixed values for properties that shouldn't matter much.
  1722. BOOL quotedMessageIsDelivered = NO;
  1723. BOOL quotedMessageIsRead = NO;
  1724. TSOutgoingMessageState quotedMessageMessageState = TSOutgoingMessageStateSent;
  1725. BOOL replyIsDelivered = NO;
  1726. BOOL replyIsRead = NO;
  1727. // Seamlessly convert oversize text messages to oversize text attachments.
  1728. if ([quotedMessageBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) {
  1729. OWSAssertDebug(!quotedMessageAssetLoader);
  1730. quotedMessageAssetLoader = [DebugUIMessagesAssetLoader oversizeTextInstanceWithText:quotedMessageBody];
  1731. quotedMessageBody = nil;
  1732. }
  1733. if (replyMessageBody &&
  1734. [replyMessageBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) {
  1735. OWSAssertDebug(!replyAssetLoader);
  1736. replyAssetLoader = [DebugUIMessagesAssetLoader oversizeTextInstanceWithText:replyMessageBody];
  1737. replyMessageBody = nil;
  1738. }
  1739. NSMutableString *label = [NSMutableString new];
  1740. [label appendString:@"Quoted Reply ("];
  1741. [label appendString:replyLabel];
  1742. if (isReplyIncoming) {
  1743. } else {
  1744. [label appendString:[self actionLabelForHasCaption:NO
  1745. outgoingMessageState:replyMessageState
  1746. isDelivered:replyIsDelivered
  1747. isRead:replyIsRead]];
  1748. }
  1749. [label appendString:@") to ("];
  1750. [label appendString:quotedMessageLabel];
  1751. if (quotedMessageAssetLoader) {
  1752. [label appendFormat:@" %@", quotedMessageAssetLoader.labelEmoji];
  1753. }
  1754. if (isQuotedMessageIncoming) {
  1755. } else {
  1756. [label appendString:[self actionLabelForHasCaption:quotedMessageBody.length > 0
  1757. outgoingMessageState:quotedMessageMessageState
  1758. isDelivered:quotedMessageIsDelivered
  1759. isRead:quotedMessageIsRead]];
  1760. }
  1761. [label appendString:@")"];
  1762. NSMutableArray<ActionPrepareBlock> *prepareBlocks = [NSMutableArray new];
  1763. if (quotedMessageAssetLoader.prepareBlock) {
  1764. [prepareBlocks addObject:quotedMessageAssetLoader.prepareBlock];
  1765. }
  1766. if (replyAssetLoader.prepareBlock) {
  1767. [prepareBlocks addObject:replyAssetLoader.prepareBlock];
  1768. }
  1769. // We don't need to configure ConversationStyle's view width in this case.
  1770. ConversationStyle *conversationStyle = [[ConversationStyle alloc] initWithThread:thread];
  1771. return [DebugUIMessagesSingleAction
  1772. actionWithLabel:label
  1773. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  1774. NSString *_Nullable quotedMessageBodyWIndex
  1775. = (quotedMessageBody ? [NSString stringWithFormat:@"%zd %@", index, quotedMessageBody] : nil);
  1776. TSQuotedMessage *_Nullable quotedMessage = nil;
  1777. if (isQuotedMessageIncoming) {
  1778. TSIncomingMessage *_Nullable messageToQuote = nil;
  1779. messageToQuote = [self createFakeIncomingMessage:thread
  1780. messageBody:quotedMessageBodyWIndex
  1781. fakeAssetLoader:quotedMessageAssetLoader
  1782. isAttachmentDownloaded:YES
  1783. quotedMessage:nil
  1784. transaction:transaction];
  1785. OWSAssertDebug(messageToQuote);
  1786. OWSLogVerbose(@"%@", label);
  1787. [DDLog flushLog];
  1788. id<ConversationViewItem> viewItem =
  1789. [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote
  1790. isGroupThread:thread.isGroupThread
  1791. transaction:transaction
  1792. conversationStyle:conversationStyle];
  1793. quotedMessage = [
  1794. [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction]
  1795. buildQuotedMessageForSending];
  1796. } else {
  1797. TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread
  1798. messageBody:quotedMessageBodyWIndex
  1799. fakeAssetLoader:quotedMessageAssetLoader
  1800. messageState:quotedMessageMessageState
  1801. isDelivered:quotedMessageIsDelivered
  1802. isRead:quotedMessageIsRead
  1803. quotedMessage:nil
  1804. contactShare:nil
  1805. linkPreview:nil
  1806. transaction:transaction];
  1807. OWSAssertDebug(messageToQuote);
  1808. id<ConversationViewItem> viewItem =
  1809. [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote
  1810. isGroupThread:thread.isGroupThread
  1811. transaction:transaction
  1812. conversationStyle:conversationStyle];
  1813. quotedMessage = [
  1814. [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction]
  1815. buildQuotedMessageForSending];
  1816. }
  1817. OWSAssertDebug(quotedMessage);
  1818. NSString *_Nullable replyMessageBodyWIndex
  1819. = (replyMessageBody ? [NSString stringWithFormat:@"%zd %@", index, replyMessageBody] : nil);
  1820. if (isReplyIncoming) {
  1821. [self createFakeIncomingMessage:thread
  1822. messageBody:replyMessageBodyWIndex
  1823. fakeAssetLoader:replyAssetLoader
  1824. isAttachmentDownloaded:NO
  1825. quotedMessage:quotedMessage
  1826. transaction:transaction];
  1827. } else {
  1828. [self createFakeOutgoingMessage:thread
  1829. messageBody:replyMessageBodyWIndex
  1830. fakeAssetLoader:replyAssetLoader
  1831. messageState:replyMessageState
  1832. isDelivered:replyIsDelivered
  1833. isRead:replyIsRead
  1834. quotedMessage:quotedMessage
  1835. contactShare:nil
  1836. linkPreview:nil
  1837. transaction:transaction];
  1838. }
  1839. }
  1840. prepareBlock:[self groupPrepareBlockWithPrepareBlocks:prepareBlocks]];
  1841. }
  1842. // Recursively perform a group of "prepare blocks" in sequence, aborting
  1843. // if any fail.
  1844. + (ActionPrepareBlock)groupPrepareBlockWithPrepareBlocks:(NSArray<ActionPrepareBlock> *)prepareBlocks
  1845. {
  1846. return ^(ActionSuccessBlock success, ActionFailureBlock failure) {
  1847. [self groupPrepareBlockStepWithPrepareBlocks:[prepareBlocks mutableCopy] success:success failure:failure];
  1848. };
  1849. }
  1850. + (void)groupPrepareBlockStepWithPrepareBlocks:(NSMutableArray<ActionPrepareBlock> *)prepareBlocks
  1851. success:(ActionSuccessBlock)success
  1852. failure:(ActionFailureBlock)failure
  1853. {
  1854. if (prepareBlocks.count < 1) {
  1855. success();
  1856. return;
  1857. }
  1858. ActionPrepareBlock nextPrepareBlock = [prepareBlocks lastObject];
  1859. [prepareBlocks removeLastObject];
  1860. nextPrepareBlock(
  1861. ^{
  1862. [self groupPrepareBlockStepWithPrepareBlocks:prepareBlocks success:success failure:failure];
  1863. },
  1864. failure);
  1865. }
  1866. + (NSArray<DebugUIMessagesAction *> *)allFakeQuotedReplyActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  1867. {
  1868. OWSAssertDebug(thread);
  1869. NSString *shortText = @"Lorem ipsum";
  1870. NSString *mediumText = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, "
  1871. @"consectetur adipiscing elit.";
  1872. NSString *longText = [self randomOversizeText];
  1873. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  1874. if (includeLabels) {
  1875. [actions addObject:[self fakeIncomingTextMessageAction:thread
  1876. text:@"⚠️ Quoted Replies (Message Lengths) ⚠️"]];
  1877. }
  1878. [actions addObjectsFromArray:@[
  1879. [self fakeQuotedReplyAction:thread
  1880. quotedMessageLabel:@"Short Text"
  1881. isQuotedMessageIncoming:NO
  1882. quotedMessageBody:shortText
  1883. quotedMessageAssetLoader:nil
  1884. replyLabel:@"Short Text"
  1885. isReplyIncoming:NO
  1886. replyMessageBody:shortText
  1887. replyAssetLoader:nil
  1888. replyMessageState:TSOutgoingMessageStateSent],
  1889. [self fakeQuotedReplyAction:thread
  1890. quotedMessageLabel:@"Short Text"
  1891. isQuotedMessageIncoming:NO
  1892. quotedMessageBody:shortText
  1893. quotedMessageAssetLoader:nil
  1894. replyLabel:@"Medium Text"
  1895. isReplyIncoming:NO
  1896. replyMessageBody:mediumText
  1897. replyAssetLoader:nil
  1898. replyMessageState:TSOutgoingMessageStateSent],
  1899. [self fakeQuotedReplyAction:thread
  1900. quotedMessageLabel:@"Medium Text"
  1901. isQuotedMessageIncoming:NO
  1902. quotedMessageBody:mediumText
  1903. quotedMessageAssetLoader:nil
  1904. replyLabel:@"Short Text"
  1905. isReplyIncoming:NO
  1906. replyMessageBody:shortText
  1907. replyAssetLoader:nil
  1908. replyMessageState:TSOutgoingMessageStateSent],
  1909. [self fakeQuotedReplyAction:thread
  1910. quotedMessageLabel:@"Medium Text"
  1911. isQuotedMessageIncoming:NO
  1912. quotedMessageBody:mediumText
  1913. quotedMessageAssetLoader:nil
  1914. replyLabel:@"Medium Text"
  1915. isReplyIncoming:NO
  1916. replyMessageBody:mediumText
  1917. replyAssetLoader:nil
  1918. replyMessageState:TSOutgoingMessageStateSent],
  1919. [self fakeQuotedReplyAction:thread
  1920. quotedMessageLabel:@"Long Text"
  1921. isQuotedMessageIncoming:NO
  1922. quotedMessageBody:longText
  1923. quotedMessageAssetLoader:nil
  1924. replyLabel:@"Long Text"
  1925. isReplyIncoming:NO
  1926. replyMessageBody:longText
  1927. replyAssetLoader:nil
  1928. replyMessageState:TSOutgoingMessageStateSent],
  1929. ]];
  1930. if (includeLabels) {
  1931. [actions addObject:[self fakeIncomingTextMessageAction:thread
  1932. text:@"⚠️ Quoted Replies (Attachment Types) ⚠️"]];
  1933. }
  1934. [actions addObjectsFromArray:@[
  1935. [self fakeQuotedReplyAction:thread
  1936. quotedMessageLabel:@"Jpg"
  1937. isQuotedMessageIncoming:NO
  1938. quotedMessageBody:nil
  1939. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  1940. replyLabel:@"Short Text"
  1941. isReplyIncoming:NO
  1942. replyMessageBody:shortText
  1943. replyAssetLoader:nil
  1944. replyMessageState:TSOutgoingMessageStateSent],
  1945. [self fakeQuotedReplyAction:thread
  1946. quotedMessageLabel:@"Jpg"
  1947. isQuotedMessageIncoming:NO
  1948. quotedMessageBody:shortText
  1949. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  1950. replyLabel:@"Short Text"
  1951. isReplyIncoming:NO
  1952. replyMessageBody:shortText
  1953. replyAssetLoader:nil
  1954. replyMessageState:TSOutgoingMessageStateSent],
  1955. [self fakeQuotedReplyAction:thread
  1956. quotedMessageLabel:@"Mp3"
  1957. isQuotedMessageIncoming:NO
  1958. quotedMessageBody:nil
  1959. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  1960. replyLabel:@"Short Text"
  1961. isReplyIncoming:NO
  1962. replyMessageBody:shortText
  1963. replyAssetLoader:nil
  1964. replyMessageState:TSOutgoingMessageStateSent],
  1965. [self fakeQuotedReplyAction:thread
  1966. quotedMessageLabel:@"Mp3"
  1967. isQuotedMessageIncoming:NO
  1968. quotedMessageBody:shortText
  1969. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  1970. replyLabel:@"Short Text"
  1971. isReplyIncoming:NO
  1972. replyMessageBody:shortText
  1973. replyAssetLoader:nil
  1974. replyMessageState:TSOutgoingMessageStateSent],
  1975. [self fakeQuotedReplyAction:thread
  1976. quotedMessageLabel:@"Mp4"
  1977. isQuotedMessageIncoming:NO
  1978. quotedMessageBody:nil
  1979. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  1980. replyLabel:@"Short Text"
  1981. isReplyIncoming:NO
  1982. replyMessageBody:shortText
  1983. replyAssetLoader:nil
  1984. replyMessageState:TSOutgoingMessageStateSent],
  1985. [self fakeQuotedReplyAction:thread
  1986. quotedMessageLabel:@"Mp4"
  1987. isQuotedMessageIncoming:NO
  1988. quotedMessageBody:shortText
  1989. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  1990. replyLabel:@"Short Text"
  1991. isReplyIncoming:NO
  1992. replyMessageBody:shortText
  1993. replyAssetLoader:nil
  1994. replyMessageState:TSOutgoingMessageStateSent],
  1995. [self fakeQuotedReplyAction:thread
  1996. quotedMessageLabel:@"Gif"
  1997. isQuotedMessageIncoming:NO
  1998. quotedMessageBody:nil
  1999. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  2000. replyLabel:@"Short Text"
  2001. isReplyIncoming:NO
  2002. replyMessageBody:shortText
  2003. replyAssetLoader:nil
  2004. replyMessageState:TSOutgoingMessageStateSent],
  2005. [self fakeQuotedReplyAction:thread
  2006. quotedMessageLabel:@"Gif"
  2007. isQuotedMessageIncoming:NO
  2008. quotedMessageBody:shortText
  2009. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  2010. replyLabel:@"Short Text"
  2011. isReplyIncoming:NO
  2012. replyMessageBody:shortText
  2013. replyAssetLoader:nil
  2014. replyMessageState:TSOutgoingMessageStateSent],
  2015. [self fakeQuotedReplyAction:thread
  2016. quotedMessageLabel:@"Pdf"
  2017. isQuotedMessageIncoming:YES
  2018. quotedMessageBody:nil
  2019. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPdfInstance]
  2020. replyLabel:@"Short Text"
  2021. isReplyIncoming:NO
  2022. replyMessageBody:shortText
  2023. replyAssetLoader:nil
  2024. replyMessageState:TSOutgoingMessageStateSent],
  2025. [self fakeQuotedReplyAction:thread
  2026. quotedMessageLabel:@"Missing Pdf"
  2027. isQuotedMessageIncoming:YES
  2028. quotedMessageBody:nil
  2029. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader missingPdfInstance]
  2030. replyLabel:@"Short Text"
  2031. isReplyIncoming:NO
  2032. replyMessageBody:shortText
  2033. replyAssetLoader:nil
  2034. replyMessageState:TSOutgoingMessageStateSent],
  2035. [self fakeQuotedReplyAction:thread
  2036. quotedMessageLabel:@"Tiny Png"
  2037. isQuotedMessageIncoming:YES
  2038. quotedMessageBody:nil
  2039. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  2040. replyLabel:@"Short Text"
  2041. isReplyIncoming:NO
  2042. replyMessageBody:shortText
  2043. replyAssetLoader:nil
  2044. replyMessageState:TSOutgoingMessageStateSent],
  2045. [self fakeQuotedReplyAction:thread
  2046. quotedMessageLabel:@"Missing Png"
  2047. isQuotedMessageIncoming:YES
  2048. quotedMessageBody:nil
  2049. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader missingPngInstance]
  2050. replyLabel:@"Short Text"
  2051. isReplyIncoming:NO
  2052. replyMessageBody:shortText
  2053. replyAssetLoader:nil
  2054. replyMessageState:TSOutgoingMessageStateSent],
  2055. ]];
  2056. if (includeLabels) {
  2057. [actions addObject:[self fakeIncomingTextMessageAction:thread
  2058. text:@"⚠️ Quoted Replies (Attachment Layout) ⚠️"]];
  2059. }
  2060. [actions addObjectsFromArray:@[
  2061. [self fakeQuotedReplyAction:thread
  2062. quotedMessageLabel:@"Tall Portrait Png"
  2063. isQuotedMessageIncoming:NO
  2064. quotedMessageBody:nil
  2065. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2066. replyLabel:@"Short Text"
  2067. isReplyIncoming:NO
  2068. replyMessageBody:shortText
  2069. replyAssetLoader:nil
  2070. replyMessageState:TSOutgoingMessageStateSent],
  2071. [self fakeQuotedReplyAction:thread
  2072. quotedMessageLabel:@"Tall Portrait Png"
  2073. isQuotedMessageIncoming:NO
  2074. quotedMessageBody:nil
  2075. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2076. replyLabel:@"Medium Text"
  2077. isReplyIncoming:NO
  2078. replyMessageBody:mediumText
  2079. replyAssetLoader:nil
  2080. replyMessageState:TSOutgoingMessageStateSent],
  2081. [self fakeQuotedReplyAction:thread
  2082. quotedMessageLabel:@"Tall Portrait Png"
  2083. isQuotedMessageIncoming:NO
  2084. quotedMessageBody:shortText
  2085. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2086. replyLabel:@"Short Text"
  2087. isReplyIncoming:NO
  2088. replyMessageBody:shortText
  2089. replyAssetLoader:nil
  2090. replyMessageState:TSOutgoingMessageStateSent],
  2091. [self fakeQuotedReplyAction:thread
  2092. quotedMessageLabel:@"Wide Landscape Png"
  2093. isQuotedMessageIncoming:NO
  2094. quotedMessageBody:nil
  2095. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2096. replyLabel:@"Short Text"
  2097. isReplyIncoming:NO
  2098. replyMessageBody:shortText
  2099. replyAssetLoader:nil
  2100. replyMessageState:TSOutgoingMessageStateSent],
  2101. [self fakeQuotedReplyAction:thread
  2102. quotedMessageLabel:@"Wide Landscape Png"
  2103. isQuotedMessageIncoming:NO
  2104. quotedMessageBody:nil
  2105. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2106. replyLabel:@"Medium Text"
  2107. isReplyIncoming:NO
  2108. replyMessageBody:mediumText
  2109. replyAssetLoader:nil
  2110. replyMessageState:TSOutgoingMessageStateSent],
  2111. [self fakeQuotedReplyAction:thread
  2112. quotedMessageLabel:@"Wide Landscape Png"
  2113. isQuotedMessageIncoming:NO
  2114. quotedMessageBody:shortText
  2115. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2116. replyLabel:@"Medium Text"
  2117. isReplyIncoming:NO
  2118. replyMessageBody:mediumText
  2119. replyAssetLoader:nil
  2120. replyMessageState:TSOutgoingMessageStateSent],
  2121. [self fakeQuotedReplyAction:thread
  2122. quotedMessageLabel:@"Tiny Png"
  2123. isQuotedMessageIncoming:YES
  2124. quotedMessageBody:nil
  2125. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  2126. replyLabel:@"Short Text"
  2127. isReplyIncoming:NO
  2128. replyMessageBody:shortText
  2129. replyAssetLoader:nil
  2130. replyMessageState:TSOutgoingMessageStateSent],
  2131. [self fakeQuotedReplyAction:thread
  2132. quotedMessageLabel:@"Tiny Png"
  2133. isQuotedMessageIncoming:YES
  2134. quotedMessageBody:nil
  2135. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  2136. replyLabel:@"Medium Text"
  2137. isReplyIncoming:NO
  2138. replyMessageBody:mediumText
  2139. replyAssetLoader:nil
  2140. replyMessageState:TSOutgoingMessageStateSent],
  2141. ]];
  2142. void (^directionActions)(BOOL, BOOL) = ^(BOOL isQuotedMessageIncoming, BOOL isReplyIncoming) {
  2143. [actions addObjectsFromArray:@[
  2144. [self fakeQuotedReplyAction:thread
  2145. quotedMessageLabel:@"Short Text"
  2146. isQuotedMessageIncoming:isQuotedMessageIncoming
  2147. quotedMessageBody:shortText
  2148. quotedMessageAssetLoader:nil
  2149. replyLabel:@"Short Text"
  2150. isReplyIncoming:isReplyIncoming
  2151. replyMessageBody:shortText
  2152. replyAssetLoader:nil
  2153. replyMessageState:TSOutgoingMessageStateSent],
  2154. ]];
  2155. };
  2156. if (includeLabels) {
  2157. [actions addObject:[self fakeIncomingTextMessageAction:thread
  2158. text:@"⚠️ Quoted Replies (Incoming v. Outgoing) ⚠️"]];
  2159. }
  2160. directionActions(NO, NO);
  2161. directionActions(YES, NO);
  2162. directionActions(NO, YES);
  2163. directionActions(YES, YES);
  2164. if (includeLabels) {
  2165. [actions addObject:[self fakeIncomingTextMessageAction:thread
  2166. text:@"⚠️ Quoted Replies (Message States) ⚠️"]];
  2167. }
  2168. [actions addObjectsFromArray:@[
  2169. [self fakeQuotedReplyAction:thread
  2170. quotedMessageLabel:@"Jpg"
  2171. isQuotedMessageIncoming:YES
  2172. quotedMessageBody:nil
  2173. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader jpegInstance]
  2174. replyLabel:@"Short Text"
  2175. isReplyIncoming:NO
  2176. replyMessageBody:shortText
  2177. replyAssetLoader:nil
  2178. replyMessageState:TSOutgoingMessageStateSent],
  2179. [self fakeQuotedReplyAction:thread
  2180. quotedMessageLabel:@"Mp3"
  2181. isQuotedMessageIncoming:YES
  2182. quotedMessageBody:nil
  2183. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp3Instance]
  2184. replyLabel:@"Short Text"
  2185. isReplyIncoming:NO
  2186. replyMessageBody:shortText
  2187. replyAssetLoader:nil
  2188. replyMessageState:TSOutgoingMessageStateSent],
  2189. [self fakeQuotedReplyAction:thread
  2190. quotedMessageLabel:@"Mp4"
  2191. isQuotedMessageIncoming:YES
  2192. quotedMessageBody:nil
  2193. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader mp4Instance]
  2194. replyLabel:@"Short Text"
  2195. isReplyIncoming:NO
  2196. replyMessageBody:shortText
  2197. replyAssetLoader:nil
  2198. replyMessageState:TSOutgoingMessageStateSent],
  2199. [self fakeQuotedReplyAction:thread
  2200. quotedMessageLabel:@"Gif"
  2201. isQuotedMessageIncoming:YES
  2202. quotedMessageBody:nil
  2203. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader gifInstance]
  2204. replyLabel:@"Short Text"
  2205. isReplyIncoming:NO
  2206. replyMessageBody:shortText
  2207. replyAssetLoader:nil
  2208. replyMessageState:TSOutgoingMessageStateSent],
  2209. [self fakeQuotedReplyAction:thread
  2210. quotedMessageLabel:@"Pdf"
  2211. isQuotedMessageIncoming:YES
  2212. quotedMessageBody:nil
  2213. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPdfInstance]
  2214. replyLabel:@"Short Text"
  2215. isReplyIncoming:NO
  2216. replyMessageBody:shortText
  2217. replyAssetLoader:nil
  2218. replyMessageState:TSOutgoingMessageStateSent],
  2219. [self fakeQuotedReplyAction:thread
  2220. quotedMessageLabel:@"Missing Pdf"
  2221. isQuotedMessageIncoming:YES
  2222. quotedMessageBody:nil
  2223. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader missingPdfInstance]
  2224. replyLabel:@"Short Text"
  2225. isReplyIncoming:NO
  2226. replyMessageBody:shortText
  2227. replyAssetLoader:nil
  2228. replyMessageState:TSOutgoingMessageStateSent],
  2229. [self fakeQuotedReplyAction:thread
  2230. quotedMessageLabel:@"Tiny Png"
  2231. isQuotedMessageIncoming:YES
  2232. quotedMessageBody:nil
  2233. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tinyPngInstance]
  2234. replyLabel:@"Short Text"
  2235. isReplyIncoming:NO
  2236. replyMessageBody:shortText
  2237. replyAssetLoader:nil
  2238. replyMessageState:TSOutgoingMessageStateSent],
  2239. [self fakeQuotedReplyAction:thread
  2240. quotedMessageLabel:@"Missing Png"
  2241. isQuotedMessageIncoming:YES
  2242. quotedMessageBody:nil
  2243. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader missingPngInstance]
  2244. replyLabel:@"Short Text"
  2245. isReplyIncoming:NO
  2246. replyMessageBody:shortText
  2247. replyAssetLoader:nil
  2248. replyMessageState:TSOutgoingMessageStateSent],
  2249. [self fakeQuotedReplyAction:thread
  2250. quotedMessageLabel:@"Short Text"
  2251. isQuotedMessageIncoming:NO
  2252. quotedMessageBody:shortText
  2253. quotedMessageAssetLoader:nil
  2254. replyLabel:@"Short Text"
  2255. isReplyIncoming:NO
  2256. replyMessageBody:shortText
  2257. replyAssetLoader:nil
  2258. replyMessageState:TSOutgoingMessageStateSending],
  2259. [self fakeQuotedReplyAction:thread
  2260. quotedMessageLabel:@"Short Text"
  2261. isQuotedMessageIncoming:NO
  2262. quotedMessageBody:shortText
  2263. quotedMessageAssetLoader:nil
  2264. replyLabel:@"Short Text"
  2265. isReplyIncoming:NO
  2266. replyMessageBody:shortText
  2267. replyAssetLoader:nil
  2268. replyMessageState:TSOutgoingMessageStateSent],
  2269. [self fakeQuotedReplyAction:thread
  2270. quotedMessageLabel:@"Short Text"
  2271. isQuotedMessageIncoming:NO
  2272. quotedMessageBody:shortText
  2273. quotedMessageAssetLoader:nil
  2274. replyLabel:@"Short Text"
  2275. isReplyIncoming:NO
  2276. replyMessageBody:shortText
  2277. replyAssetLoader:nil
  2278. replyMessageState:TSOutgoingMessageStateFailed],
  2279. ]];
  2280. if (includeLabels) {
  2281. [actions addObject:[self fakeIncomingTextMessageAction:thread
  2282. text:@"⚠️ Quoted Replies (Reply W. Attachment) ⚠️"]];
  2283. }
  2284. [actions addObjectsFromArray:@[
  2285. // Png + Text -> Png + Text
  2286. [self fakeQuotedReplyAction:thread
  2287. quotedMessageLabel:@"Tall Portrait Png"
  2288. isQuotedMessageIncoming:NO
  2289. quotedMessageBody:shortText
  2290. quotedMessageAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2291. replyLabel:@"Tall Portrait Png"
  2292. isReplyIncoming:NO
  2293. replyMessageBody:shortText
  2294. replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2295. replyMessageState:TSOutgoingMessageStateSent],
  2296. // Text -> Png + Text
  2297. [self fakeQuotedReplyAction:thread
  2298. quotedMessageLabel:@"Short Text"
  2299. isQuotedMessageIncoming:NO
  2300. quotedMessageBody:shortText
  2301. quotedMessageAssetLoader:nil
  2302. replyLabel:@"Tall Portrait Png"
  2303. isReplyIncoming:NO
  2304. replyMessageBody:shortText
  2305. replyAssetLoader:nil
  2306. replyMessageState:TSOutgoingMessageStateSent],
  2307. // Text -> Png
  2308. [self fakeQuotedReplyAction:thread
  2309. quotedMessageLabel:@"Short Text"
  2310. isQuotedMessageIncoming:NO
  2311. quotedMessageBody:shortText
  2312. quotedMessageAssetLoader:nil
  2313. replyLabel:@"Tall Portrait Png"
  2314. isReplyIncoming:NO
  2315. replyMessageBody:nil
  2316. replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2317. replyMessageState:TSOutgoingMessageStateSent],
  2318. // Png -> Png + Text
  2319. [self fakeQuotedReplyAction:thread
  2320. quotedMessageLabel:@"Short Text"
  2321. isQuotedMessageIncoming:NO
  2322. quotedMessageBody:shortText
  2323. quotedMessageAssetLoader:nil
  2324. replyLabel:@"Tall Portrait Png"
  2325. isReplyIncoming:NO
  2326. replyMessageBody:shortText
  2327. replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2328. replyMessageState:TSOutgoingMessageStateSent],
  2329. // Png -> Portrait Png + Text
  2330. [self fakeQuotedReplyAction:thread
  2331. quotedMessageLabel:@"Short Text"
  2332. isQuotedMessageIncoming:NO
  2333. quotedMessageBody:shortText
  2334. quotedMessageAssetLoader:nil
  2335. replyLabel:@"Tall Portrait Png"
  2336. isReplyIncoming:NO
  2337. replyMessageBody:shortText
  2338. replyAssetLoader:[DebugUIMessagesAssetLoader tallPortraitPngInstance]
  2339. replyMessageState:TSOutgoingMessageStateSent],
  2340. // Png -> Landscape Png + Text
  2341. [self fakeQuotedReplyAction:thread
  2342. quotedMessageLabel:@"Short Text"
  2343. isQuotedMessageIncoming:NO
  2344. quotedMessageBody:shortText
  2345. quotedMessageAssetLoader:nil
  2346. replyLabel:@"Wide Landscape Png"
  2347. isReplyIncoming:NO
  2348. replyMessageBody:shortText
  2349. replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2350. replyMessageState:TSOutgoingMessageStateSent],
  2351. // Png -> Landscape Png + Text
  2352. [self fakeQuotedReplyAction:thread
  2353. quotedMessageLabel:@"Short Text"
  2354. isQuotedMessageIncoming:NO
  2355. quotedMessageBody:shortText
  2356. quotedMessageAssetLoader:nil
  2357. replyLabel:@"Wide Landscape Png + Short Text"
  2358. isReplyIncoming:NO
  2359. replyMessageBody:shortText
  2360. replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2361. replyMessageState:TSOutgoingMessageStateSent],
  2362. // Png -> Landscape Png + Text
  2363. [self fakeQuotedReplyAction:thread
  2364. quotedMessageLabel:@"Short Text"
  2365. isQuotedMessageIncoming:NO
  2366. quotedMessageBody:shortText
  2367. quotedMessageAssetLoader:nil
  2368. replyLabel:@"Wide Landscape Png + Short Text"
  2369. isReplyIncoming:NO
  2370. replyMessageBody:shortText
  2371. replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2372. replyMessageState:TSOutgoingMessageStateSent],
  2373. // Png -> Landscape Png + Text
  2374. [self fakeQuotedReplyAction:thread
  2375. quotedMessageLabel:@"Short Text"
  2376. isQuotedMessageIncoming:NO
  2377. quotedMessageBody:shortText
  2378. quotedMessageAssetLoader:nil
  2379. replyLabel:@"Wide Landscape Png + Medium Text"
  2380. isReplyIncoming:NO
  2381. replyMessageBody:mediumText
  2382. replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2383. replyMessageState:TSOutgoingMessageStateSent],
  2384. // Png -> Landscape Png + Text
  2385. [self fakeQuotedReplyAction:thread
  2386. quotedMessageLabel:@"Short Text"
  2387. isQuotedMessageIncoming:NO
  2388. quotedMessageBody:shortText
  2389. quotedMessageAssetLoader:nil
  2390. replyLabel:@"Wide Landscape Png + Medium Text"
  2391. isReplyIncoming:NO
  2392. replyMessageBody:mediumText
  2393. replyAssetLoader:[DebugUIMessagesAssetLoader wideLandscapePngInstance]
  2394. replyMessageState:TSOutgoingMessageStateSent],
  2395. ]];
  2396. return actions;
  2397. }
  2398. + (DebugUIMessagesAction *)allQuotedReplyAction:(TSThread *)thread
  2399. {
  2400. OWSAssertDebug(thread);
  2401. return
  2402. [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Quoted Reply"
  2403. subactions:[self allFakeQuotedReplyActions:thread includeLabels:YES]];
  2404. }
  2405. + (void)selectQuotedReplyAction:(TSThread *)thread
  2406. {
  2407. OWSAssertIsOnMainThread();
  2408. OWSAssertDebug(thread);
  2409. [self selectActionUI:[self allFakeQuotedReplyActions:thread includeLabels:NO] label:@"Select QuotedReply"];
  2410. }
  2411. + (DebugUIMessagesAction *)randomQuotedReplyAction:(TSThread *)thread
  2412. {
  2413. OWSAssertDebug(thread);
  2414. return [DebugUIMessagesGroupAction
  2415. randomGroupActionWithLabel:@"Random Quoted Reply"
  2416. subactions:[self allFakeQuotedReplyActions:thread includeLabels:NO]];
  2417. }
  2418. #pragma mark - Exemplary
  2419. + (NSArray<DebugUIMessagesAction *> *)allFakeActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  2420. {
  2421. OWSAssertDebug(thread);
  2422. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  2423. [actions addObjectsFromArray:[self allFakeMediaActions:thread includeLabels:includeLabels]];
  2424. [actions addObjectsFromArray:[self allFakeTextActions:thread includeLabels:includeLabels]];
  2425. [actions addObjectsFromArray:[self allFakeSequenceActions:thread includeLabels:includeLabels]];
  2426. [actions addObjectsFromArray:[self allFakeQuotedReplyActions:thread includeLabels:includeLabels]];
  2427. [actions addObjectsFromArray:[self allFakeBackDatedActions:thread includeLabels:includeLabels]];
  2428. [actions addObjectsFromArray:[self allFakeContactShareActions:thread includeLabels:includeLabels]];
  2429. return actions;
  2430. }
  2431. + (DebugUIMessagesAction *)allFakeAction:(TSThread *)thread
  2432. {
  2433. OWSAssertDebug(thread);
  2434. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake"
  2435. subactions:[self allFakeActions:thread includeLabels:YES]];
  2436. }
  2437. + (void)selectFakeAction:(TSThread *)thread
  2438. {
  2439. OWSAssertIsOnMainThread();
  2440. OWSAssertDebug(thread);
  2441. [self selectActionUI:[self allFakeActions:thread includeLabels:NO] label:@"Select Fake"];
  2442. }
  2443. + (void)selectActionUI:(NSArray<DebugUIMessagesAction *> *)actions label:(NSString *)label
  2444. {
  2445. OWSAssertIsOnMainThread();
  2446. UIAlertController *alert =
  2447. [UIAlertController alertControllerWithTitle:label message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  2448. for (DebugUIMessagesAction *action in actions) {
  2449. [alert addAction:[UIAlertAction actionWithTitle:action.label
  2450. style:UIAlertActionStyleDefault
  2451. handler:^(UIAlertAction *ignore) {
  2452. [self performActionNTimes:action];
  2453. }]];
  2454. }
  2455. [alert addAction:[OWSAlerts cancelAction]];
  2456. UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController];
  2457. [fromViewController presentViewController:alert animated:YES completion:nil];
  2458. }
  2459. #pragma mark - Sequences
  2460. + (NSArray<DebugUIMessagesAction *> *)allFakeSequenceActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  2461. {
  2462. OWSAssertDebug(thread);
  2463. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  2464. if (includeLabels) {
  2465. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2466. messageState:TSOutgoingMessageStateSent
  2467. text:@"⚠️ Short Message Sequences ⚠️"]];
  2468. }
  2469. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming"]];
  2470. [actions
  2471. addObject:[self fakeOutgoingTextMessageAction:thread messageState:TSOutgoingMessageStateSent text:@"Outgoing"]];
  2472. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 1"]];
  2473. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 2"]];
  2474. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming 3"]];
  2475. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2476. messageState:TSOutgoingMessageStateFailed
  2477. text:@"Outgoing Unsent 1"]];
  2478. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2479. messageState:TSOutgoingMessageStateFailed
  2480. text:@"Outgoing Unsent 2"]];
  2481. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2482. messageState:TSOutgoingMessageStateSending
  2483. text:@"Outgoing Sending 1"]];
  2484. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2485. messageState:TSOutgoingMessageStateSending
  2486. text:@"Outgoing Sending 2"]];
  2487. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2488. messageState:TSOutgoingMessageStateSent
  2489. text:@"Outgoing Sent 1"]];
  2490. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2491. messageState:TSOutgoingMessageStateSent
  2492. text:@"Outgoing Sent 2"]];
  2493. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2494. text:@"Outgoing Delivered 1"
  2495. messageState:TSOutgoingMessageStateSent
  2496. isDelivered:YES
  2497. isRead:NO]];
  2498. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2499. text:@"Outgoing Delivered 2"
  2500. messageState:TSOutgoingMessageStateSent
  2501. isDelivered:YES
  2502. isRead:NO]];
  2503. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2504. text:@"Outgoing Read 1"
  2505. messageState:TSOutgoingMessageStateSent
  2506. isDelivered:YES
  2507. isRead:YES]];
  2508. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2509. text:@"Outgoing Read 2"
  2510. messageState:TSOutgoingMessageStateSent
  2511. isDelivered:YES
  2512. isRead:YES]];
  2513. [actions addObject:[self fakeIncomingTextMessageAction:thread text:@"Incoming"]];
  2514. if (includeLabels) {
  2515. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2516. messageState:TSOutgoingMessageStateSent
  2517. text:@"⚠️ Long Message Sequences ⚠️"]];
  2518. }
  2519. NSString *longText = @"\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse rutrum, nulla "
  2520. @"vitae pretium hendrerit, tellus turpis pharetra libero...";
  2521. [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming" stringByAppendingString:longText]]];
  2522. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2523. messageState:TSOutgoingMessageStateSent
  2524. text:[@"Outgoing" stringByAppendingString:longText]]];
  2525. [actions
  2526. addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming 1" stringByAppendingString:longText]]];
  2527. [actions
  2528. addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming 2" stringByAppendingString:longText]]];
  2529. [actions
  2530. addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming 3" stringByAppendingString:longText]]];
  2531. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2532. messageState:TSOutgoingMessageStateFailed
  2533. text:[@"Outgoing Unsent 1" stringByAppendingString:longText]]];
  2534. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2535. messageState:TSOutgoingMessageStateFailed
  2536. text:[@"Outgoing Unsent 2" stringByAppendingString:longText]]];
  2537. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2538. messageState:TSOutgoingMessageStateSending
  2539. text:[@"Outgoing Sending 1" stringByAppendingString:longText]]];
  2540. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2541. messageState:TSOutgoingMessageStateSending
  2542. text:[@"Outgoing Sending 2" stringByAppendingString:longText]]];
  2543. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2544. messageState:TSOutgoingMessageStateSent
  2545. text:[@"Outgoing Sent 1" stringByAppendingString:longText]]];
  2546. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2547. messageState:TSOutgoingMessageStateSent
  2548. text:[@"Outgoing Sent 2" stringByAppendingString:longText]]];
  2549. [actions
  2550. addObject:[self fakeShortOutgoingTextMessageAction:thread
  2551. text:[@"Outgoing Delivered 1" stringByAppendingString:longText]
  2552. messageState:TSOutgoingMessageStateSent
  2553. isDelivered:YES
  2554. isRead:NO]];
  2555. [actions
  2556. addObject:[self fakeShortOutgoingTextMessageAction:thread
  2557. text:[@"Outgoing Delivered 2" stringByAppendingString:longText]
  2558. messageState:TSOutgoingMessageStateSent
  2559. isDelivered:YES
  2560. isRead:NO]];
  2561. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2562. text:[@"Outgoing Read 1" stringByAppendingString:longText]
  2563. messageState:TSOutgoingMessageStateSent
  2564. isDelivered:YES
  2565. isRead:YES]];
  2566. [actions addObject:[self fakeShortOutgoingTextMessageAction:thread
  2567. text:[@"Outgoing Read 2" stringByAppendingString:longText]
  2568. messageState:TSOutgoingMessageStateSent
  2569. isDelivered:YES
  2570. isRead:YES]];
  2571. [actions addObject:[self fakeIncomingTextMessageAction:thread text:[@"Incoming" stringByAppendingString:longText]]];
  2572. return actions;
  2573. }
  2574. + (DebugUIMessagesAction *)allFakeSequencesAction:(TSThread *)thread
  2575. {
  2576. OWSAssertDebug(thread);
  2577. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Sequences"
  2578. subactions:[self allFakeSequenceActions:thread includeLabels:YES]];
  2579. }
  2580. #pragma mark - Back-dated
  2581. + (DebugUIMessagesAction *)fakeBackDatedMessageAction:(TSThread *)thread
  2582. label:(NSString *)label
  2583. dateOffset:(int64_t)dateOffset
  2584. {
  2585. OWSAssertDebug(thread);
  2586. return [DebugUIMessagesSingleAction
  2587. actionWithLabel:[NSString stringWithFormat:@"Fake Back-Date Message (%@)", label]
  2588. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  2589. NSString *messageBody =
  2590. [[@(index).stringValue stringByAppendingString:@" "] stringByAppendingString:self.randomText];
  2591. TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread
  2592. messageBody:messageBody
  2593. fakeAssetLoader:nil
  2594. messageState:TSOutgoingMessageStateSent
  2595. isDelivered:NO
  2596. isRead:NO
  2597. quotedMessage:nil
  2598. contactShare:nil
  2599. linkPreview:nil
  2600. transaction:transaction];
  2601. [message setReceivedAtTimestamp:(uint64_t)((int64_t)[NSDate ows_millisecondTimeStamp] + dateOffset)];
  2602. [message saveWithTransaction:transaction];
  2603. }];
  2604. }
  2605. + (NSArray<DebugUIMessagesAction *> *)allFakeBackDatedActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  2606. {
  2607. OWSAssertDebug(thread);
  2608. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  2609. if (includeLabels) {
  2610. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2611. messageState:TSOutgoingMessageStateSent
  2612. text:@"⚠️ Back-Dated ⚠️"]];
  2613. }
  2614. [actions
  2615. addObject:[self fakeBackDatedMessageAction:thread label:@"One Minute Ago" dateOffset:-(int64_t)kMinuteInMs]];
  2616. [actions addObject:[self fakeBackDatedMessageAction:thread label:@"One Hour Ago" dateOffset:-(int64_t)kHourInMs]];
  2617. [actions addObject:[self fakeBackDatedMessageAction:thread label:@"One Day Ago" dateOffset:-(int64_t)kDayInMs]];
  2618. [actions
  2619. addObject:[self fakeBackDatedMessageAction:thread label:@"Two Days Ago" dateOffset:-(int64_t)kDayInMs * 2]];
  2620. [actions
  2621. addObject:[self fakeBackDatedMessageAction:thread label:@"Ten Days Ago" dateOffset:-(int64_t)kDayInMs * 10]];
  2622. [actions
  2623. addObject:[self fakeBackDatedMessageAction:thread label:@"400 Days Ago" dateOffset:-(int64_t)kDayInMs * 400]];
  2624. return actions;
  2625. }
  2626. + (DebugUIMessagesAction *)allFakeBackDatedAction:(TSThread *)thread
  2627. {
  2628. OWSAssertDebug(thread);
  2629. return [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Back-Dated"
  2630. subactions:[self allFakeBackDatedActions:thread includeLabels:YES]];
  2631. }
  2632. + (void)selectBackDatedAction:(TSThread *)thread
  2633. {
  2634. OWSAssertIsOnMainThread();
  2635. OWSAssertDebug(thread);
  2636. [self selectActionUI:[self allFakeBackDatedActions:thread includeLabels:NO] label:@"Select Back-Dated"];
  2637. }
  2638. #pragma mark - Contact Shares
  2639. typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transaction);
  2640. + (DebugUIMessagesAction *)fakeContactShareMessageAction:(TSThread *)thread
  2641. label:(NSString *)label
  2642. contactBlock:(OWSContactBlock)contactBlock
  2643. {
  2644. OWSAssertDebug(thread);
  2645. return [DebugUIMessagesSingleAction
  2646. actionWithLabel:[NSString stringWithFormat:@"Fake Contact Share (%@)", label]
  2647. unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
  2648. OWSContact *contact = contactBlock(transaction);
  2649. TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread
  2650. messageBody:nil
  2651. fakeAssetLoader:nil
  2652. messageState:TSOutgoingMessageStateSent
  2653. isDelivered:NO
  2654. isRead:NO
  2655. quotedMessage:nil
  2656. contactShare:contact
  2657. linkPreview:nil
  2658. transaction:transaction];
  2659. [message saveWithTransaction:transaction];
  2660. }];
  2661. }
  2662. + (NSArray<DebugUIMessagesAction *> *)allFakeContactShareActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  2663. {
  2664. OWSAssertDebug(thread);
  2665. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  2666. if (includeLabels) {
  2667. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2668. messageState:TSOutgoingMessageStateSent
  2669. text:@"⚠️ Share Contact ⚠️"]];
  2670. }
  2671. [actions addObject:[self fakeContactShareMessageAction:thread
  2672. label:@"Name & Number"
  2673. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2674. OWSContact *contact = [OWSContact new];
  2675. OWSContactName *name = [OWSContactName new];
  2676. contact.name = name;
  2677. name.givenName = @"Alice";
  2678. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2679. phoneNumber.phoneType = OWSContactPhoneType_Home;
  2680. phoneNumber.phoneNumber = @"+13213214321";
  2681. contact.phoneNumbers = @[
  2682. phoneNumber,
  2683. ];
  2684. return contact;
  2685. }]];
  2686. [actions addObject:[self fakeContactShareMessageAction:thread
  2687. label:@"Name & Email"
  2688. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2689. OWSContact *contact = [OWSContact new];
  2690. OWSContactName *name = [OWSContactName new];
  2691. contact.name = name;
  2692. name.givenName = @"Bob";
  2693. OWSContactEmail *email = [OWSContactEmail new];
  2694. email.emailType = OWSContactEmailType_Home;
  2695. email.email = @"a@b.com";
  2696. contact.emails = @[
  2697. email,
  2698. ];
  2699. return contact;
  2700. }]];
  2701. [actions addObject:[self fakeContactShareMessageAction:thread
  2702. label:@"Complicated"
  2703. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2704. OWSContact *contact = [OWSContact new];
  2705. OWSContactName *name = [OWSContactName new];
  2706. contact.name = name;
  2707. name.givenName = @"Alice";
  2708. name.familyName = @"Carol";
  2709. name.middleName = @"Bob";
  2710. name.namePrefix = @"Ms.";
  2711. name.nameSuffix = @"Esq.";
  2712. name.organizationName = @"Falafel Hut";
  2713. OWSContactPhoneNumber *phoneNumber1 = [OWSContactPhoneNumber new];
  2714. phoneNumber1.phoneType = OWSContactPhoneType_Home;
  2715. phoneNumber1.phoneNumber = @"+13213215555";
  2716. OWSContactPhoneNumber *phoneNumber2 = [OWSContactPhoneNumber new];
  2717. phoneNumber2.phoneType = OWSContactPhoneType_Custom;
  2718. phoneNumber2.label = @"Carphone";
  2719. phoneNumber2.phoneNumber = @"+13332226666";
  2720. contact.phoneNumbers = @[
  2721. phoneNumber1,
  2722. phoneNumber2,
  2723. ];
  2724. NSMutableArray<OWSContactEmail *> *emails = [NSMutableArray new];
  2725. for (NSUInteger i = 0; i < 16; i++) {
  2726. OWSContactEmail *email = [OWSContactEmail new];
  2727. email.emailType = OWSContactEmailType_Home;
  2728. email.email = [NSString stringWithFormat:@"a%zd@b.com", i];
  2729. [emails addObject:email];
  2730. }
  2731. contact.emails = emails;
  2732. OWSContactAddress *address1 = [OWSContactAddress new];
  2733. address1.addressType = OWSContactAddressType_Home;
  2734. address1.street = @"123 home st.";
  2735. address1.neighborhood = @"round the bend.";
  2736. address1.city = @"homeville";
  2737. address1.region = @"HO";
  2738. address1.postcode = @"12345";
  2739. address1.country = @"USA";
  2740. OWSContactAddress *address2 = [OWSContactAddress new];
  2741. address2.addressType = OWSContactAddressType_Custom;
  2742. address2.label = @"Otra casa";
  2743. address2.pobox = @"caja 123";
  2744. address2.street = @"123 casa calle";
  2745. address2.city = @"barrio norte";
  2746. address2.region = @"AB";
  2747. address2.postcode = @"53421";
  2748. address2.country = @"MX";
  2749. contact.addresses = @[
  2750. address1,
  2751. address2,
  2752. ];
  2753. UIImage *avatarImage =
  2754. [OWSAvatarBuilder buildRandomAvatarWithDiameter:200];
  2755. [contact saveAvatarImage:avatarImage transaction:transaction];
  2756. return contact;
  2757. }]];
  2758. [actions addObject:[self fakeContactShareMessageAction:thread
  2759. label:@"Long values"
  2760. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2761. OWSContact *contact = [OWSContact new];
  2762. OWSContactName *name = [OWSContactName new];
  2763. contact.name = name;
  2764. name.givenName = @"Bobasdjasdlkjasldkjas";
  2765. name.familyName = @"Bobasdjasdlkjasldkjas";
  2766. OWSContactEmail *email = [OWSContactEmail new];
  2767. email.emailType = OWSContactEmailType_Mobile;
  2768. email.email = @"asdlakjsaldkjasldkjasdlkjasdlkjasdlkajsa@b.com";
  2769. contact.emails = @[
  2770. email,
  2771. ];
  2772. return contact;
  2773. }]];
  2774. [actions addObject:[self fakeContactShareMessageAction:thread
  2775. label:@"System Contact w/o Signal"
  2776. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2777. OWSContact *contact = [OWSContact new];
  2778. OWSContactName *name = [OWSContactName new];
  2779. contact.name = name;
  2780. name.givenName = @"Add Me To Your Contacts";
  2781. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2782. phoneNumber.phoneType = OWSContactPhoneType_Work;
  2783. phoneNumber.phoneNumber = @"+324602053911";
  2784. contact.phoneNumbers = @[
  2785. phoneNumber,
  2786. ];
  2787. return contact;
  2788. }]];
  2789. [actions addObject:[self fakeContactShareMessageAction:thread
  2790. label:@"System Contact w. Signal"
  2791. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2792. OWSContact *contact = [OWSContact new];
  2793. OWSContactName *name = [OWSContactName new];
  2794. contact.name = name;
  2795. name.givenName = @"Add Me To Your Contacts";
  2796. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2797. phoneNumber.phoneType = OWSContactPhoneType_Work;
  2798. phoneNumber.phoneNumber = @"+32460205392";
  2799. contact.phoneNumbers = @[
  2800. phoneNumber,
  2801. ];
  2802. return contact;
  2803. }]];
  2804. return actions;
  2805. }
  2806. + (DebugUIMessagesAction *)fakeAllContactShareAction:(TSThread *)thread
  2807. {
  2808. OWSAssertDebug(thread);
  2809. return
  2810. [DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Contact Shares"
  2811. subactions:[self allFakeContactShareActions:thread includeLabels:YES]];
  2812. }
  2813. + (DebugUIMessagesAction *)sendContactShareMessageAction:(TSThread *)thread
  2814. label:(NSString *)label
  2815. contactBlock:(OWSContactBlock)contactBlock
  2816. {
  2817. OWSAssertDebug(thread);
  2818. return [DebugUIMessagesSingleAction
  2819. actionWithLabel:[NSString stringWithFormat:@"Send Contact Share (%@)", label]
  2820. staggeredActionBlock:^(NSUInteger index,
  2821. YapDatabaseReadWriteTransaction *transaction,
  2822. ActionSuccessBlock success,
  2823. ActionFailureBlock failure) {
  2824. OWSContact *contact = contactBlock(transaction);
  2825. OWSLogVerbose(@"sending contact: %@", contact.debugDescription);
  2826. [ThreadUtil enqueueMessageWithContactShare:contact inThread:thread];
  2827. success();
  2828. }];
  2829. }
  2830. + (NSArray<DebugUIMessagesAction *> *)allSendContactShareActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
  2831. {
  2832. OWSAssertDebug(thread);
  2833. NSMutableArray<DebugUIMessagesAction *> *actions = [NSMutableArray new];
  2834. if (includeLabels) {
  2835. [actions addObject:[self fakeOutgoingTextMessageAction:thread
  2836. messageState:TSOutgoingMessageStateSent
  2837. text:@"⚠️ Send Share Contact ⚠️"]];
  2838. }
  2839. [actions addObject:[self sendContactShareMessageAction:thread
  2840. label:@"Name & Number"
  2841. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2842. OWSContact *contact = [OWSContact new];
  2843. OWSContactName *name = [OWSContactName new];
  2844. contact.name = name;
  2845. name.givenName = @"Alice";
  2846. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2847. phoneNumber.phoneType = OWSContactPhoneType_Home;
  2848. phoneNumber.phoneNumber = @"+13213214321";
  2849. contact.phoneNumbers = @[
  2850. phoneNumber,
  2851. ];
  2852. return contact;
  2853. }]];
  2854. [actions addObject:[self sendContactShareMessageAction:thread
  2855. label:@"Name & Email"
  2856. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2857. OWSContact *contact = [OWSContact new];
  2858. OWSContactName *name = [OWSContactName new];
  2859. contact.name = name;
  2860. name.givenName = @"Bob";
  2861. OWSContactEmail *email = [OWSContactEmail new];
  2862. email.emailType = OWSContactEmailType_Home;
  2863. email.email = @"a@b.com";
  2864. contact.emails = @[
  2865. email,
  2866. ];
  2867. return contact;
  2868. }]];
  2869. [actions addObject:[self sendContactShareMessageAction:thread
  2870. label:@"Complicated"
  2871. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2872. OWSContact *contact = [OWSContact new];
  2873. OWSContactName *name = [OWSContactName new];
  2874. contact.name = name;
  2875. name.givenName = @"Alice";
  2876. name.familyName = @"Carol";
  2877. name.middleName = @"Bob";
  2878. name.namePrefix = @"Ms.";
  2879. name.nameSuffix = @"Esq.";
  2880. name.organizationName = @"Falafel Hut";
  2881. OWSContactPhoneNumber *phoneNumber1 = [OWSContactPhoneNumber new];
  2882. phoneNumber1.phoneType = OWSContactPhoneType_Home;
  2883. phoneNumber1.phoneNumber = @"+13213214321";
  2884. OWSContactPhoneNumber *phoneNumber2 = [OWSContactPhoneNumber new];
  2885. phoneNumber2.phoneType = OWSContactPhoneType_Custom;
  2886. phoneNumber2.label = @"Carphone";
  2887. phoneNumber2.phoneNumber = @"+13332221111";
  2888. contact.phoneNumbers = @[
  2889. phoneNumber1,
  2890. phoneNumber2,
  2891. ];
  2892. NSMutableArray<OWSContactEmail *> *emails = [NSMutableArray new];
  2893. for (NSUInteger i = 0; i < 16; i++) {
  2894. OWSContactEmail *email = [OWSContactEmail new];
  2895. email.emailType = OWSContactEmailType_Home;
  2896. email.email = [NSString stringWithFormat:@"a%zd@b.com", i];
  2897. [emails addObject:email];
  2898. }
  2899. contact.emails = emails;
  2900. OWSContactAddress *address1 = [OWSContactAddress new];
  2901. address1.addressType = OWSContactAddressType_Home;
  2902. address1.street = @"123 home st.";
  2903. address1.neighborhood = @"round the bend.";
  2904. address1.city = @"homeville";
  2905. address1.region = @"HO";
  2906. address1.postcode = @"12345";
  2907. address1.country = @"USA";
  2908. OWSContactAddress *address2 = [OWSContactAddress new];
  2909. address2.addressType = OWSContactAddressType_Custom;
  2910. address2.label = @"Otra casa";
  2911. address2.pobox = @"caja 123";
  2912. address2.street = @"123 casa calle";
  2913. address2.city = @"barrio norte";
  2914. address2.region = @"AB";
  2915. address2.postcode = @"53421";
  2916. address2.country = @"MX";
  2917. contact.addresses = @[
  2918. address1,
  2919. address2,
  2920. ];
  2921. UIImage *avatarImage =
  2922. [OWSAvatarBuilder buildRandomAvatarWithDiameter:200];
  2923. [contact saveAvatarImage:avatarImage transaction:transaction];
  2924. return contact;
  2925. }]];
  2926. [actions addObject:[self sendContactShareMessageAction:thread
  2927. label:@"Long values"
  2928. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2929. OWSContact *contact = [OWSContact new];
  2930. OWSContactName *name = [OWSContactName new];
  2931. contact.name = name;
  2932. name.givenName = @"Bobasdjasdlkjasldkjas";
  2933. name.familyName = @"Bobasdjasdlkjasldkjas";
  2934. OWSContactEmail *email = [OWSContactEmail new];
  2935. email.emailType = OWSContactEmailType_Mobile;
  2936. email.email = @"asdlakjsaldkjasldkjasdlkjasdlkjasdlkajsa@b.com";
  2937. contact.emails = @[
  2938. email,
  2939. ];
  2940. return contact;
  2941. }]];
  2942. [actions addObject:[self sendContactShareMessageAction:thread
  2943. label:@"System Contact w/o Signal"
  2944. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2945. OWSContact *contact = [OWSContact new];
  2946. OWSContactName *name = [OWSContactName new];
  2947. contact.name = name;
  2948. name.givenName = @"Add Me To Your Contacts";
  2949. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2950. phoneNumber.phoneType = OWSContactPhoneType_Work;
  2951. phoneNumber.phoneNumber = @"+324602053911";
  2952. contact.phoneNumbers = @[
  2953. phoneNumber,
  2954. ];
  2955. return contact;
  2956. }]];
  2957. [actions addObject:[self sendContactShareMessageAction:thread
  2958. label:@"System Contact w. Signal"
  2959. contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  2960. OWSContact *contact = [OWSContact new];
  2961. OWSContactName *name = [OWSContactName new];
  2962. contact.name = name;
  2963. name.givenName = @"Add Me To Your Contacts";
  2964. OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
  2965. phoneNumber.phoneType = OWSContactPhoneType_Work;
  2966. phoneNumber.phoneNumber = @"+32460205392";
  2967. contact.phoneNumbers = @[
  2968. phoneNumber,
  2969. ];
  2970. return contact;
  2971. }]];
  2972. return actions;
  2973. }
  2974. + (void)sendAllContacts:(TSThread *)thread
  2975. {
  2976. NSArray<DebugUIMessagesAction *> *subactions = [self allSendContactShareActions:thread includeLabels:NO];
  2977. DebugUIMessagesAction *action =
  2978. [DebugUIMessagesGroupAction allGroupActionWithLabel:@"Send All Contact Shares" subactions:subactions];
  2979. [action prepareAndPerformNTimes:subactions.count];
  2980. }
  2981. #pragma mark -
  2982. + (NSString *)randomOversizeText
  2983. {
  2984. NSMutableString *message = [NSMutableString new];
  2985. while (message.length < kOversizeTextMessageSizeThreshold) {
  2986. [message appendString:@"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse rutrum, nulla "
  2987. @"vitae pretium hendrerit, tellus turpis pharetra libero, vitae sodales tortor ante vel "
  2988. @"sem. Fusce sed nisl a lorem gravida tincidunt. Suspendisse efficitur non quam ac "
  2989. @"sodales. Aenean ut velit maximus, posuere sem a, accumsan nunc. Donec ullamcorper "
  2990. @"turpis lorem. Quisque dignissim purus eu placerat ultricies. Proin at urna eget mi "
  2991. @"semper congue. Aenean non elementum ex. Praesent pharetra quam at sem vestibulum, "
  2992. @"vestibulum ornare dolor elementum. Vestibulum massa tortor, scelerisque sit amet "
  2993. @"pulvinar a, rhoncus vitae nisl. Sed mi nunc, tempus at varius in, malesuada vitae "
  2994. @"dui. Vivamus efficitur pulvinar erat vitae congue. Proin vehicula turpis non felis "
  2995. @"congue facilisis. Nullam aliquet dapibus ligula ac mollis. Etiam sit amet posuere "
  2996. @"lorem, in rhoncus nisi.\n\n"];
  2997. }
  2998. return message;
  2999. }
  3000. + (void)sendOversizeTextMessage:(TSThread *)thread
  3001. {
  3002. [self sendAttachment:nil thread:thread messageBody:[self randomOversizeText]];
  3003. }
  3004. + (NSData *)createRandomNSDataOfSize:(size_t)size
  3005. {
  3006. OWSAssertDebug(size % 4 == 0);
  3007. OWSAssertDebug(size < INT_MAX);
  3008. return [Randomness generateRandomBytes:(int)size];
  3009. }
  3010. + (void)sendRandomAttachment:(TSThread *)thread uti:(NSString *)uti
  3011. {
  3012. [self sendRandomAttachment:thread uti:uti length:256];
  3013. }
  3014. + (NSString *)randomCaptionText
  3015. {
  3016. return [NSString stringWithFormat:@"%@ (caption)", [self randomText]];
  3017. }
  3018. + (void)sendRandomAttachment:(TSThread *)thread uti:(NSString *)uti length:(NSUInteger)length
  3019. {
  3020. DataSource *_Nullable dataSource =
  3021. [DataSourceValue dataSourceWithData:[self createRandomNSDataOfSize:length] utiType:uti];
  3022. SignalAttachment *attachment =
  3023. [SignalAttachment attachmentWithDataSource:dataSource dataUTI:uti imageQuality:TSImageQualityOriginal];
  3024. if (arc4random_uniform(100) > 50) {
  3025. // give 1/2 our attachments captions, and add a hint that it's a caption since we
  3026. // style them indistinguishably from a separate text message.
  3027. attachment.captionText = [self randomCaptionText];
  3028. }
  3029. [self sendAttachment:attachment thread:thread messageBody:nil];
  3030. }
  3031. + (SSKProtoEnvelope *)createEnvelopeForThread:(TSThread *)thread
  3032. {
  3033. OWSAssertDebug(thread);
  3034. uint64_t timestamp = [NSDate ows_millisecondTimeStamp];
  3035. NSString *source = ^{
  3036. if ([thread isKindOfClass:[TSGroupThread class]]) {
  3037. TSGroupThread *gThread = (TSGroupThread *)thread;
  3038. return gThread.groupModel.groupMemberIds[0];
  3039. } else if ([thread isKindOfClass:[TSContactThread class]]) {
  3040. TSContactThread *contactThread = (TSContactThread *)thread;
  3041. return contactThread.contactIdentifier;
  3042. } else {
  3043. OWSFailDebug(@"failure: unknown thread type");
  3044. return @"unknown-source-id";
  3045. }
  3046. }();
  3047. SSKProtoEnvelopeBuilder *envelopeBuilder = [SSKProtoEnvelope builderWithType:SSKProtoEnvelopeTypeCiphertext
  3048. timestamp:timestamp];
  3049. [envelopeBuilder setSource:source];
  3050. [envelopeBuilder setSourceDevice:1];
  3051. NSError *error;
  3052. SSKProtoEnvelope *_Nullable envelope = [envelopeBuilder buildAndReturnError:&error];
  3053. if (error || !envelope) {
  3054. OWSFailDebug(@"Could not construct envelope: %@.", error);
  3055. return nil;
  3056. }
  3057. return envelope;
  3058. }
  3059. + (NSArray<TSInteraction *> *)unsavedSystemMessagesInThread:(TSThread *)thread
  3060. {
  3061. OWSAssertDebug(thread);
  3062. NSMutableArray<TSInteraction *> *result = [NSMutableArray new];
  3063. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3064. if ([thread isKindOfClass:[TSContactThread class]]) {
  3065. TSContactThread *contactThread = (TSContactThread *)thread;
  3066. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3067. withCallNumber:@"+19174054215"
  3068. callType:RPRecentCallTypeIncoming
  3069. inThread:contactThread]];
  3070. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3071. withCallNumber:@"+19174054215"
  3072. callType:RPRecentCallTypeOutgoing
  3073. inThread:contactThread]];
  3074. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3075. withCallNumber:@"+19174054215"
  3076. callType:RPRecentCallTypeIncomingMissed
  3077. inThread:contactThread]];
  3078. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3079. withCallNumber:@"+19174054215"
  3080. callType:RPRecentCallTypeIncomingMissedBecauseOfChangedIdentity
  3081. inThread:contactThread]];
  3082. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3083. withCallNumber:@"+19174054215"
  3084. callType:RPRecentCallTypeOutgoingIncomplete
  3085. inThread:contactThread]];
  3086. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3087. withCallNumber:@"+19174054215"
  3088. callType:RPRecentCallTypeIncomingIncomplete
  3089. inThread:contactThread]];
  3090. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3091. withCallNumber:@"+19174054215"
  3092. callType:RPRecentCallTypeIncomingDeclined
  3093. inThread:contactThread]];
  3094. [result addObject:[[TSCall alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3095. withCallNumber:@"+19174054215"
  3096. callType:RPRecentCallTypeOutgoingMissed
  3097. inThread:contactThread]];
  3098. }
  3099. {
  3100. NSNumber *durationSeconds = [OWSDisappearingMessagesConfiguration validDurationsSeconds][0];
  3101. OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
  3102. [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
  3103. enabled:YES
  3104. durationSeconds:(uint32_t)[durationSeconds intValue]];
  3105. // MJK - should be safe to remove this senderTimestamp
  3106. [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
  3107. initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3108. thread:thread
  3109. configuration:disappearingMessagesConfiguration
  3110. createdByRemoteName:@"Alice"
  3111. createdInExistingGroup:NO]];
  3112. }
  3113. {
  3114. NSNumber *durationSeconds = [OWSDisappearingMessagesConfiguration validDurationsSeconds][0];
  3115. OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
  3116. [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
  3117. enabled:YES
  3118. durationSeconds:(uint32_t)[durationSeconds intValue]];
  3119. // MJK - should be safe to remove this senderTimestamp
  3120. [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
  3121. initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3122. thread:thread
  3123. configuration:disappearingMessagesConfiguration
  3124. createdByRemoteName:nil
  3125. createdInExistingGroup:YES]];
  3126. }
  3127. {
  3128. NSNumber *durationSeconds = [[OWSDisappearingMessagesConfiguration validDurationsSeconds] lastObject];
  3129. OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
  3130. [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
  3131. enabled:YES
  3132. durationSeconds:(uint32_t)[durationSeconds intValue]];
  3133. // MJK TODO - remove senderTimestamp
  3134. [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
  3135. initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3136. thread:thread
  3137. configuration:disappearingMessagesConfiguration
  3138. createdByRemoteName:@"Alice"
  3139. createdInExistingGroup:NO]];
  3140. }
  3141. {
  3142. OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
  3143. [[OWSDisappearingMessagesConfiguration alloc] initWithThreadId:thread.uniqueId
  3144. enabled:NO
  3145. durationSeconds:0];
  3146. // MJK TODO - remove senderTimestamp
  3147. [result addObject:[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
  3148. initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3149. thread:thread
  3150. configuration:disappearingMessagesConfiguration
  3151. createdByRemoteName:@"Alice"
  3152. createdInExistingGroup:NO]];
  3153. }
  3154. [result addObject:[TSInfoMessage userNotRegisteredMessageInThread:thread recipientId:@"+19174054215"]];
  3155. // MJK - should be safe to remove this senderTimestamp
  3156. [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3157. inThread:thread
  3158. messageType:TSInfoMessageTypeSessionDidEnd]];
  3159. // TODO: customMessage?
  3160. // MJK - should be safe to remove this senderTimestamp
  3161. [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3162. inThread:thread
  3163. messageType:TSInfoMessageTypeGroupUpdate]];
  3164. // TODO: customMessage?
  3165. // MJK - should be safe to remove this senderTimestamp
  3166. [result addObject:[[TSInfoMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3167. inThread:thread
  3168. messageType:TSInfoMessageTypeGroupQuit]];
  3169. // MJK - should be safe to remove this senderTimestamp
  3170. [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3171. thread:thread
  3172. recipientId:@"+19174054215"
  3173. verificationState:OWSVerificationStateDefault
  3174. isLocalChange:YES]];
  3175. // MJK - should be safe to remove this senderTimestamp
  3176. [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3177. thread:thread
  3178. recipientId:@"+19174054215"
  3179. verificationState:OWSVerificationStateVerified
  3180. isLocalChange:YES]];
  3181. // MJK - should be safe to remove this senderTimestamp
  3182. [result
  3183. addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3184. thread:thread
  3185. recipientId:@"+19174054215"
  3186. verificationState:OWSVerificationStateNoLongerVerified
  3187. isLocalChange:YES]];
  3188. // MJK - should be safe to remove this senderTimestamp
  3189. [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3190. thread:thread
  3191. recipientId:@"+19174054215"
  3192. verificationState:OWSVerificationStateDefault
  3193. isLocalChange:NO]];
  3194. // MJK - should be safe to remove this senderTimestamp
  3195. [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3196. thread:thread
  3197. recipientId:@"+19174054215"
  3198. verificationState:OWSVerificationStateVerified
  3199. isLocalChange:NO]];
  3200. // MJK - should be safe to remove this senderTimestamp
  3201. [result
  3202. addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3203. thread:thread
  3204. recipientId:@"+19174054215"
  3205. verificationState:OWSVerificationStateNoLongerVerified
  3206. isLocalChange:NO]];
  3207. [result addObject:[TSErrorMessage missingSessionWithEnvelope:[self createEnvelopeForThread:thread]
  3208. withTransaction:transaction]];
  3209. [result addObject:[TSErrorMessage invalidKeyExceptionWithEnvelope:[self createEnvelopeForThread:thread]
  3210. withTransaction:transaction]];
  3211. [result addObject:[TSErrorMessage invalidVersionWithEnvelope:[self createEnvelopeForThread:thread]
  3212. withTransaction:transaction]];
  3213. [result addObject:[TSErrorMessage corruptedMessageWithEnvelope:[self createEnvelopeForThread:thread]
  3214. withTransaction:transaction]];
  3215. #pragma clang diagnostic push
  3216. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  3217. TSInvalidIdentityKeyReceivingErrorMessage *_Nullable blockingSNChangeMessage =
  3218. [TSInvalidIdentityKeyReceivingErrorMessage untrustedKeyWithEnvelope:[self createEnvelopeForThread:thread]
  3219. withTransaction:transaction];
  3220. #pragma clang diagnostic pop
  3221. OWSAssertDebug(blockingSNChangeMessage);
  3222. [result addObject:blockingSNChangeMessage];
  3223. // MJK TODO - should be safe to remove this senderTimestamp
  3224. [result addObject:[[TSErrorMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3225. inThread:thread
  3226. failedMessageType:TSErrorMessageNonBlockingIdentityChange
  3227. recipientId:@"+19174054215"]];
  3228. }];
  3229. return result;
  3230. }
  3231. + (void)createSystemMessagesInThread:(TSThread *)thread
  3232. {
  3233. OWSAssertDebug(thread);
  3234. NSArray<TSInteraction *> *messages = [self unsavedSystemMessagesInThread:thread];
  3235. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3236. for (TSInteraction *message in messages) {
  3237. [message saveWithTransaction:transaction];
  3238. }
  3239. }];
  3240. }
  3241. + (void)createSystemMessageInThread:(TSThread *)thread
  3242. {
  3243. OWSAssertDebug(thread);
  3244. NSArray<TSInteraction *> *messages = [self unsavedSystemMessagesInThread:thread];
  3245. TSInteraction *message = messages[(NSUInteger)arc4random_uniform((uint32_t)messages.count)];
  3246. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3247. [message saveWithTransaction:transaction];
  3248. }];
  3249. }
  3250. + (void)sendTextAndSystemMessages:(NSUInteger)counter thread:(TSThread *)thread
  3251. {
  3252. if (counter < 1) {
  3253. return;
  3254. }
  3255. if (arc4random_uniform(2) == 0) {
  3256. [self sendTextMessageInThread:thread counter:counter];
  3257. } else {
  3258. [self createSystemMessageInThread:thread];
  3259. }
  3260. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  3261. [self sendTextAndSystemMessages:counter - 1 thread:thread];
  3262. });
  3263. }
  3264. + (NSString *)randomText
  3265. {
  3266. NSArray<NSString *> *randomTexts = @[
  3267. @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ",
  3268. (@"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
  3269. @"Suspendisse rutrum, nulla vitae pretium hendrerit, tellus "
  3270. @"turpis pharetra libero, vitae sodales tortor ante vel sem."),
  3271. @"In a time of universal deceit - telling the truth is a revolutionary act.",
  3272. @"If you want a vision of the future, imagine a boot stamping on a human face - forever.",
  3273. @"Who controls the past controls the future. Who controls the present controls the past.",
  3274. @"All animals are equal, but some animals are more equal than others.",
  3275. @"War is peace. Freedom is slavery. Ignorance is strength.",
  3276. (@"All the war-propaganda, all the screaming and lies and hatred, comes invariably from people who are not "
  3277. @"fighting."),
  3278. (@"Political language. . . is designed to make lies sound truthful and murder respectable, and to give an "
  3279. @"appearance of solidity to pure wind."),
  3280. (@"The nationalist not only does not disapprove of atrocities committed by his own side, but he has a "
  3281. @"remarkable capacity for not even hearing about them."),
  3282. (@"Every generation imagines itself to be more intelligent than the one that went before it, and wiser than "
  3283. @"the "
  3284. @"one that comes after it."),
  3285. @"War against a foreign country only happens when the moneyed classes think they are going to profit from it.",
  3286. @"People have only as much liberty as they have the intelligence to want and the courage to take.",
  3287. (@"You cannot buy the revolution. You cannot make the revolution. You can only be the revolution. It is in your "
  3288. @"spirit, or it is nowhere."),
  3289. (@"That is what I have always understood to be the essence of anarchism: the conviction that the burden of "
  3290. @"proof has to be placed on authority, and that it should be dismantled if that burden cannot be met."),
  3291. (@"Ask for work. If they don't give you work, ask for bread. If they do not give you work or bread, then take "
  3292. @"bread."),
  3293. @"Every society has the criminals it deserves.",
  3294. (@"Anarchism is founded on the observation that since few men are wise enough to rule themselves, even fewer "
  3295. @"are wise enough to rule others."),
  3296. @"If you would know who controls you see who you may not criticise.",
  3297. @"At one time in the world there were woods that no one owned."
  3298. ];
  3299. NSString *randomText = randomTexts[(NSUInteger)arc4random_uniform((uint32_t)randomTexts.count)];
  3300. return randomText;
  3301. }
  3302. + (void)createFakeThreads:(NSUInteger)threadCount withFakeMessages:(NSUInteger)messageCount
  3303. {
  3304. [DebugContactsUtils
  3305. createRandomContacts:threadCount
  3306. contactHandler:^(CNContact *_Nonnull contact, NSUInteger idx, BOOL *_Nonnull stop) {
  3307. NSString *phoneNumberText = contact.phoneNumbers.firstObject.value.stringValue;
  3308. OWSAssertDebug(phoneNumberText);
  3309. PhoneNumber *phoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:phoneNumberText];
  3310. OWSAssertDebug(phoneNumber);
  3311. OWSAssertDebug(phoneNumber.toE164);
  3312. TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:phoneNumber.toE164];
  3313. [self sendFakeMessages:messageCount thread:contactThread];
  3314. OWSLogError(@"Create fake thread: %@, interactions: %lu",
  3315. phoneNumber.toE164,
  3316. (unsigned long)contactThread.numberOfInteractions);
  3317. }];
  3318. }
  3319. + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread
  3320. {
  3321. [self sendFakeMessages:counter thread:thread isTextOnly:NO];
  3322. }
  3323. + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread isTextOnly:(BOOL)isTextOnly
  3324. {
  3325. const NSUInteger kMaxBatchSize = 2500;
  3326. if (counter < kMaxBatchSize) {
  3327. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3328. [self sendFakeMessages:counter thread:thread isTextOnly:isTextOnly transaction:transaction];
  3329. }];
  3330. } else {
  3331. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  3332. NSUInteger remainder = counter;
  3333. while (remainder > 0) {
  3334. NSUInteger batchSize = MIN(kMaxBatchSize, remainder);
  3335. [OWSPrimaryStorage.dbReadWriteConnection
  3336. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3337. [self sendFakeMessages:batchSize thread:thread isTextOnly:isTextOnly transaction:transaction];
  3338. }];
  3339. remainder -= batchSize;
  3340. OWSLogInfo(@"sendFakeMessages %lu / %lu", (unsigned long)(counter - remainder), (unsigned long)counter);
  3341. }
  3342. });
  3343. }
  3344. }
  3345. + (void)thrashInsertAndDeleteForThread:(TSThread *)thread counter:(NSUInteger)counter
  3346. {
  3347. if (counter == 0) {
  3348. return;
  3349. }
  3350. uint32_t sendDelay = arc4random_uniform((uint32_t)(0.01 * NSEC_PER_SEC));
  3351. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, sendDelay), dispatch_get_main_queue(), ^{
  3352. [self sendFakeMessages:1 thread:thread];
  3353. });
  3354. uint32_t deleteDelay = arc4random_uniform((uint32_t)(0.01 * NSEC_PER_SEC));
  3355. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, deleteDelay), dispatch_get_main_queue(), ^{
  3356. [OWSPrimaryStorage.sharedManager.dbReadWriteConnection
  3357. asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
  3358. [self deleteRandomMessages:1 thread:thread transaction:transaction];
  3359. }];
  3360. [self thrashInsertAndDeleteForThread:thread counter:counter - 1];
  3361. });
  3362. }
  3363. // TODO: Remove.
  3364. + (void)sendFakeMessages:(NSUInteger)counter
  3365. thread:(TSThread *)thread
  3366. isTextOnly:(BOOL)isTextOnly
  3367. transaction:(YapDatabaseReadWriteTransaction *)transaction
  3368. {
  3369. OWSLogInfo(@"sendFakeMessages: %lu", (unsigned long)counter);
  3370. for (NSUInteger i = 0; i < counter; i++) {
  3371. NSString *randomText = [[self randomText] stringByAppendingFormat:@" (sequence: %lu)", (unsigned long)i + 1];
  3372. switch (arc4random_uniform(isTextOnly ? 2 : 4)) {
  3373. case 0: {
  3374. // MJK - should be safe to remove this senderTimestamp
  3375. TSIncomingMessage *message =
  3376. [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3377. inThread:thread
  3378. authorId:@"+19174054215"
  3379. sourceDeviceId:0
  3380. messageBody:randomText
  3381. attachmentIds:@[]
  3382. expiresInSeconds:0
  3383. quotedMessage:nil
  3384. contactShare:nil
  3385. linkPreview:nil
  3386. serverTimestamp:nil
  3387. wasReceivedByUD:NO];
  3388. [message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
  3389. break;
  3390. }
  3391. case 1: {
  3392. [self createFakeOutgoingMessage:thread
  3393. messageBody:randomText
  3394. fakeAssetLoader:nil
  3395. messageState:TSOutgoingMessageStateFailed
  3396. isDelivered:NO
  3397. isRead:NO
  3398. quotedMessage:nil
  3399. contactShare:nil
  3400. linkPreview:nil
  3401. transaction:transaction];
  3402. break;
  3403. }
  3404. case 2: {
  3405. UInt32 filesize = 64;
  3406. TSAttachmentPointer *pointer =
  3407. [[TSAttachmentPointer alloc] initWithServerId:237391539706350548
  3408. key:[self createRandomNSDataOfSize:filesize]
  3409. digest:nil
  3410. byteCount:filesize
  3411. contentType:@"audio/mp3"
  3412. sourceFilename:@"test.mp3"
  3413. caption:nil
  3414. albumMessageId:nil
  3415. attachmentType:TSAttachmentTypeDefault
  3416. mediaSize:CGSizeZero];
  3417. pointer.state = TSAttachmentPointerStateFailed;
  3418. [pointer saveWithTransaction:transaction];
  3419. // MJK - should be safe to remove this senderTimestamp
  3420. TSIncomingMessage *message =
  3421. [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3422. inThread:thread
  3423. authorId:@"+19174054215"
  3424. sourceDeviceId:0
  3425. messageBody:nil
  3426. attachmentIds:@[
  3427. pointer.uniqueId,
  3428. ]
  3429. expiresInSeconds:0
  3430. quotedMessage:nil
  3431. contactShare:nil
  3432. linkPreview:nil
  3433. serverTimestamp:nil
  3434. wasReceivedByUD:NO];
  3435. [message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
  3436. break;
  3437. }
  3438. case 3: {
  3439. NSString *filename = @"test.mp3";
  3440. UInt32 filesize = 16;
  3441. TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3"
  3442. byteCount:filesize
  3443. sourceFilename:filename
  3444. caption:nil
  3445. albumMessageId:nil];
  3446. NSError *error;
  3447. BOOL success = [attachmentStream writeData:[self createRandomNSDataOfSize:filesize] error:&error];
  3448. OWSAssertDebug(success && !error);
  3449. [attachmentStream saveWithTransaction:transaction];
  3450. [self createFakeOutgoingMessage:thread
  3451. messageBody:nil
  3452. attachmentId:attachmentStream.uniqueId
  3453. filename:filename
  3454. messageState:TSOutgoingMessageStateFailed
  3455. isDelivered:NO
  3456. isRead:NO
  3457. isVoiceMessage:NO
  3458. quotedMessage:nil
  3459. contactShare:nil
  3460. linkPreview:nil
  3461. transaction:transaction];
  3462. break;
  3463. }
  3464. }
  3465. }
  3466. }
  3467. #pragma mark -
  3468. + (void)createNewGroups:(NSUInteger)counter recipientId:(NSString *)recipientId
  3469. {
  3470. if (counter < 1) {
  3471. return;
  3472. }
  3473. NSString *groupName = [NSUUID UUID].UUIDString;
  3474. NSMutableArray<NSString *> *recipientIds = [@[
  3475. recipientId,
  3476. [TSAccountManager localNumber],
  3477. ] mutableCopy];
  3478. NSData *groupId = [Randomness generateRandomBytes:kGroupIdLength];
  3479. TSGroupModel *groupModel =
  3480. [[TSGroupModel alloc] initWithTitle:groupName memberIds:recipientIds image:nil groupId:groupId];
  3481. __block TSGroupThread *thread;
  3482. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
  3483. thread = [TSGroupThread getOrCreateThreadWithGroupModel:groupModel transaction:transaction];
  3484. OWSAssertDebug(thread);
  3485. TSOutgoingMessage *message = [TSOutgoingMessage outgoingMessageInThread:thread
  3486. groupMetaMessage:TSGroupMetaMessageNew
  3487. expiresInSeconds:0];
  3488. [message updateWithCustomMessage:NSLocalizedString(@"GROUP_CREATED", nil) transaction:transaction];
  3489. [self.messageSenderJobQueue addMessage:message transaction:transaction];
  3490. }];
  3491. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  3492. [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3493. [ThreadUtil enqueueMessageWithText:[@(counter) description]
  3494. inThread:thread
  3495. quotedReplyModel:nil
  3496. linkPreviewDraft:nil
  3497. transaction:transaction];
  3498. }];
  3499. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  3500. [self createNewGroups:counter - 1 recipientId:recipientId];
  3501. });
  3502. });
  3503. }
  3504. + (void)injectFakeIncomingMessages:(NSUInteger)counter thread:(TSThread *)thread
  3505. {
  3506. // Wait 5 seconds so debug user has time to navigate to another
  3507. // view before message processing occurs.
  3508. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.f * NSEC_PER_SEC)),
  3509. dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
  3510. ^{
  3511. for (NSUInteger i = 0; i < counter; i++) {
  3512. [self injectIncomingMessageInThread:thread counter:counter - i];
  3513. }
  3514. });
  3515. }
  3516. + (void)injectIncomingMessageInThread:(TSThread *)thread counter:(NSUInteger)counter
  3517. {
  3518. OWSAssertDebug(thread);
  3519. OWSLogInfo(@"injectIncomingMessageInThread: %lu", (unsigned long)counter);
  3520. NSString *randomText = [self randomText];
  3521. NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
  3522. SSKProtoDataMessageBuilder *dataMessageBuilder = [SSKProtoDataMessage builder];
  3523. [dataMessageBuilder setBody:text];
  3524. if ([thread isKindOfClass:[TSGroupThread class]]) {
  3525. TSGroupThread *groupThread = (TSGroupThread *)thread;
  3526. SSKProtoGroupContextBuilder *groupBuilder =
  3527. [SSKProtoGroupContext builderWithId:groupThread.groupModel.groupId type:SSKProtoGroupContextTypeDeliver];
  3528. [dataMessageBuilder setGroup:groupBuilder.buildIgnoringErrors];
  3529. }
  3530. SSKProtoContentBuilder *payloadBuilder = [SSKProtoContent builder];
  3531. [payloadBuilder setDataMessage:dataMessageBuilder.buildIgnoringErrors];
  3532. NSData *plaintextData = [payloadBuilder buildIgnoringErrors].serializedDataIgnoringErrors;
  3533. // Try to use an arbitrary member of the current thread that isn't
  3534. // ourselves as the sender.
  3535. NSString *_Nullable recipientId = [[thread recipientIdentifiers] firstObject];
  3536. // This might be an "empty" group with no other members. If so, use a fake
  3537. // sender id.
  3538. if (!recipientId) {
  3539. recipientId = @"+12345678901";
  3540. }
  3541. uint64_t timestamp = [NSDate ows_millisecondTimeStamp];
  3542. NSString *source = recipientId;
  3543. uint32_t sourceDevice = 1;
  3544. SSKProtoEnvelopeType envelopeType = SSKProtoEnvelopeTypeCiphertext;
  3545. NSData *content = plaintextData;
  3546. SSKProtoEnvelopeBuilder *envelopeBuilder = [SSKProtoEnvelope builderWithType:envelopeType timestamp:timestamp];
  3547. [envelopeBuilder setSource:source];
  3548. [envelopeBuilder setSourceDevice:sourceDevice];
  3549. envelopeBuilder.content = content;
  3550. NSError *error;
  3551. NSData *_Nullable envelopeData = [envelopeBuilder buildSerializedDataAndReturnError:&error];
  3552. if (error || !envelopeData) {
  3553. OWSFailDebug(@"Could not serialize envelope: %@.", error);
  3554. return;
  3555. }
  3556. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3557. [SSKEnvironment.shared.batchMessageProcessor enqueueEnvelopeData:envelopeData
  3558. plaintextData:plaintextData
  3559. wasReceivedByUD:NO
  3560. transaction:transaction];
  3561. }];
  3562. }
  3563. + (void)performRandomActions:(NSUInteger)counter thread:(TSThread *)thread
  3564. {
  3565. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)),
  3566. dispatch_get_main_queue(),
  3567. ^{
  3568. [self performRandomActionInThread:thread counter:counter];
  3569. if (counter > 0) {
  3570. [self performRandomActions:counter - 1 thread:thread];
  3571. }
  3572. });
  3573. }
  3574. + (void)performRandomActionInThread:(TSThread *)thread counter:(NSUInteger)counter
  3575. {
  3576. typedef void (^TransactionBlock)(YapDatabaseReadWriteTransaction *transaction);
  3577. NSArray<TransactionBlock> *actionBlocks = @[
  3578. ^(YapDatabaseReadWriteTransaction *transaction) {
  3579. // injectIncomingMessageInThread doesn't take a transaction.
  3580. dispatch_async(dispatch_get_main_queue(), ^{
  3581. [self injectIncomingMessageInThread:thread counter:counter];
  3582. });
  3583. },
  3584. ^(YapDatabaseReadWriteTransaction *transaction) {
  3585. // sendTextMessageInThread doesn't take a transaction.
  3586. dispatch_async(dispatch_get_main_queue(), ^{
  3587. [self sendTextMessageInThread:thread counter:counter];
  3588. });
  3589. },
  3590. ^(YapDatabaseReadWriteTransaction *transaction) {
  3591. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3592. [self sendFakeMessages:messageCount thread:thread isTextOnly:NO transaction:transaction];
  3593. },
  3594. ^(YapDatabaseReadWriteTransaction *transaction) {
  3595. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3596. [self deleteRandomMessages:messageCount thread:thread transaction:transaction];
  3597. },
  3598. ^(YapDatabaseReadWriteTransaction *transaction) {
  3599. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3600. [self deleteLastMessages:messageCount thread:thread transaction:transaction];
  3601. },
  3602. ^(YapDatabaseReadWriteTransaction *transaction) {
  3603. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3604. [self deleteRandomRecentMessages:messageCount thread:thread transaction:transaction];
  3605. },
  3606. ^(YapDatabaseReadWriteTransaction *transaction) {
  3607. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3608. [self insertAndDeleteNewOutgoingMessages:messageCount thread:thread transaction:transaction];
  3609. },
  3610. ^(YapDatabaseReadWriteTransaction *transaction) {
  3611. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3612. [self resurrectNewOutgoingMessages1:messageCount thread:thread transaction:transaction];
  3613. },
  3614. ^(YapDatabaseReadWriteTransaction *transaction) {
  3615. NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4));
  3616. [self resurrectNewOutgoingMessages2:messageCount thread:thread transaction:transaction];
  3617. },
  3618. ];
  3619. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3620. NSUInteger actionCount = 1 + (NSUInteger)arc4random_uniform(3);
  3621. for (NSUInteger actionIdx = 0; actionIdx < actionCount; actionIdx++) {
  3622. TransactionBlock actionBlock = actionBlocks[(NSUInteger)arc4random_uniform((uint32_t)actionBlocks.count)];
  3623. actionBlock(transaction);
  3624. }
  3625. }];
  3626. }
  3627. + (void)deleteRandomMessages:(NSUInteger)count
  3628. thread:(TSThread *)thread
  3629. transaction:(YapDatabaseReadWriteTransaction *)transaction
  3630. {
  3631. OWSLogInfo(@"deleteRandomMessages: %zd", count);
  3632. YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
  3633. NSUInteger messageCount = [interactionsByThread numberOfItemsInGroup:thread.uniqueId];
  3634. NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
  3635. for (NSUInteger messageIdx = 0; messageIdx < messageCount; messageIdx++) {
  3636. [messageIndices addObject:@(messageIdx)];
  3637. }
  3638. NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
  3639. for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) {
  3640. NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count);
  3641. NSNumber *messageIdx = messageIndices[idx];
  3642. [messageIndices removeObjectAtIndex:idx];
  3643. TSInteraction *_Nullable interaction =
  3644. [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
  3645. OWSAssertDebug(interaction);
  3646. [interactions addObject:interaction];
  3647. }
  3648. for (TSInteraction *interaction in interactions) {
  3649. [interaction removeWithTransaction:transaction];
  3650. }
  3651. }
  3652. + (void)deleteLastMessages:(NSUInteger)count
  3653. thread:(TSThread *)thread
  3654. transaction:(YapDatabaseReadWriteTransaction *)transaction
  3655. {
  3656. OWSLogInfo(@"deleteLastMessages");
  3657. YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
  3658. NSUInteger messageCount = (NSUInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId];
  3659. NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
  3660. for (NSUInteger i = 0; i < count && i < messageCount; i++) {
  3661. NSUInteger messageIdx = messageCount - (1 + i);
  3662. [messageIndices addObject:@(messageIdx)];
  3663. }
  3664. NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
  3665. for (NSNumber *messageIdx in messageIndices) {
  3666. TSInteraction *_Nullable interaction =
  3667. [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
  3668. OWSAssertDebug(interaction);
  3669. [interactions addObject:interaction];
  3670. }
  3671. for (TSInteraction *interaction in interactions) {
  3672. [interaction removeWithTransaction:transaction];
  3673. }
  3674. }
  3675. + (void)deleteRandomRecentMessages:(NSUInteger)count
  3676. thread:(TSThread *)thread
  3677. transaction:(YapDatabaseReadWriteTransaction *)transaction
  3678. {
  3679. OWSLogInfo(@"deleteRandomRecentMessages: %zd", count);
  3680. YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
  3681. NSInteger messageCount = (NSInteger)[interactionsByThread numberOfItemsInGroup:thread.uniqueId];
  3682. NSMutableArray<NSNumber *> *messageIndices = [NSMutableArray new];
  3683. const NSInteger kRecentMessageCount = 10;
  3684. for (NSInteger i = 0; i < kRecentMessageCount; i++) {
  3685. NSInteger messageIdx = messageCount - (1 + i);
  3686. if (messageIdx >= 0) {
  3687. [messageIndices addObject:@(messageIdx)];
  3688. }
  3689. }
  3690. NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
  3691. for (NSUInteger i = 0; i < count && messageIndices.count > 0; i++) {
  3692. NSUInteger idx = (NSUInteger)arc4random_uniform((uint32_t)messageIndices.count);
  3693. NSNumber *messageIdx = messageIndices[idx];
  3694. [messageIndices removeObjectAtIndex:idx];
  3695. TSInteraction *_Nullable interaction =
  3696. [interactionsByThread objectAtIndex:messageIdx.unsignedIntegerValue inGroup:thread.uniqueId];
  3697. OWSAssertDebug(interaction);
  3698. [interactions addObject:interaction];
  3699. }
  3700. for (TSInteraction *interaction in interactions) {
  3701. [interaction removeWithTransaction:transaction];
  3702. }
  3703. }
  3704. + (void)insertAndDeleteNewOutgoingMessages:(NSUInteger)count
  3705. thread:(TSThread *)thread
  3706. transaction:(YapDatabaseReadWriteTransaction *)transaction
  3707. {
  3708. OWSLogInfo(@"insertAndDeleteNewOutgoingMessages: %zd", count);
  3709. NSMutableArray<TSOutgoingMessage *> *messages = [NSMutableArray new];
  3710. for (NSUInteger i =0; i < count; i++) {
  3711. NSString *text = [self randomText];
  3712. OWSDisappearingMessagesConfiguration *configuration =
  3713. [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId transaction:transaction];
  3714. uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
  3715. TSOutgoingMessage *message = [TSOutgoingMessage outgoingMessageInThread:thread
  3716. messageBody:text
  3717. attachmentId:nil
  3718. expiresInSeconds:expiresInSeconds];
  3719. OWSLogError(@"insertAndDeleteNewOutgoingMessages timestamp: %llu.", message.timestamp);
  3720. [messages addObject:message];
  3721. }
  3722. for (TSOutgoingMessage *message in messages) {
  3723. [message saveWithTransaction:transaction];
  3724. }
  3725. for (TSOutgoingMessage *message in messages) {
  3726. [message removeWithTransaction:transaction];
  3727. }
  3728. }
  3729. + (void)resurrectNewOutgoingMessages1:(NSUInteger)count
  3730. thread:(TSThread *)thread
  3731. transaction:(YapDatabaseReadWriteTransaction *)initialTransaction
  3732. {
  3733. OWSLogInfo(@"resurrectNewOutgoingMessages1.1: %zd", count);
  3734. NSMutableArray<TSOutgoingMessage *> *messages = [NSMutableArray new];
  3735. for (NSUInteger i =0; i < count; i++) {
  3736. NSString *text = [self randomText];
  3737. OWSDisappearingMessagesConfiguration *configuration =
  3738. [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId
  3739. transaction:initialTransaction];
  3740. uint32_t expiresInSeconds = (configuration.isEnabled ? configuration.durationSeconds : 0);
  3741. TSOutgoingMessage *message = [TSOutgoingMessage outgoingMessageInThread:thread
  3742. messageBody:text
  3743. attachmentId:nil
  3744. expiresInSeconds:expiresInSeconds];
  3745. OWSLogError(@"resurrectNewOutgoingMessages1 timestamp: %llu.", message.timestamp);
  3746. [messages addObject:message];
  3747. }
  3748. for (TSOutgoingMessage *message in messages) {
  3749. [message saveWithTransaction:initialTransaction];
  3750. }
  3751. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  3752. OWSLogInfo(@"resurrectNewOutgoingMessages1.2: %zd", count);
  3753. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3754. for (TSOutgoingMessage *message in messages) {
  3755. [message removeWithTransaction:transaction];
  3756. }
  3757. for (TSOutgoingMessage *message in messages) {
  3758. [message saveWithTransaction:transaction];
  3759. }
  3760. }];
  3761. });
  3762. }
  3763. + (void)resurrectNewOutgoingMessages2:(NSUInteger)count
  3764. thread:(TSThread *)thread
  3765. transaction:(YapDatabaseReadWriteTransaction *)initialTransaction
  3766. {
  3767. OWSLogInfo(@"resurrectNewOutgoingMessages2.1: %zd", count);
  3768. NSMutableArray<TSOutgoingMessage *> *messages = [NSMutableArray new];
  3769. for (NSUInteger i =0; i < count; i++) {
  3770. NSString *text = [self randomText];
  3771. OWSDisappearingMessagesConfiguration *configuration =
  3772. [OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:thread.uniqueId
  3773. transaction:initialTransaction];
  3774. // MJK TODO - remove senderTimestamp
  3775. TSOutgoingMessage *message = [[TSOutgoingMessage alloc]
  3776. initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
  3777. inThread:thread
  3778. messageBody:text
  3779. attachmentIds:[NSMutableArray new]
  3780. expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds
  3781. : 0)expireStartedAt:0
  3782. isVoiceMessage:NO
  3783. groupMetaMessage:TSGroupMetaMessageUnspecified
  3784. quotedMessage:nil
  3785. contactShare:nil
  3786. linkPreview:nil];
  3787. OWSLogError(@"resurrectNewOutgoingMessages2 timestamp: %llu.", message.timestamp);
  3788. [messages addObject:message];
  3789. }
  3790. for (TSOutgoingMessage *message in messages) {
  3791. [message updateWithFakeMessageState:TSOutgoingMessageStateSending transaction:initialTransaction];
  3792. [message saveWithTransaction:initialTransaction];
  3793. }
  3794. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  3795. OWSLogInfo(@"resurrectNewOutgoingMessages2.2: %zd", count);
  3796. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3797. for (TSOutgoingMessage *message in messages) {
  3798. [message removeWithTransaction:transaction];
  3799. }
  3800. }];
  3801. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  3802. OWSLogInfo(@"resurrectNewOutgoingMessages2.3: %zd", count);
  3803. [OWSPrimaryStorage.dbReadWriteConnection
  3804. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3805. for (TSOutgoingMessage *message in messages) {
  3806. [message saveWithTransaction:transaction];
  3807. }
  3808. }];
  3809. });
  3810. });
  3811. }
  3812. + (void)createTimestampMessagesInThread:(TSThread *)thread
  3813. {
  3814. OWSAssertDebug(thread);
  3815. long long now = (long long)[NSDate ows_millisecondTimeStamp];
  3816. NSArray<NSNumber *> *timestamps = @[
  3817. @(now + 1 * (long long)kHourInMs),
  3818. @(now),
  3819. @(now - 1 * (long long)kHourInMs),
  3820. @(now - 12 * (long long)kHourInMs),
  3821. @(now - 1 * (long long)kDayInMs),
  3822. @(now - 2 * (long long)kDayInMs),
  3823. @(now - 3 * (long long)kDayInMs),
  3824. @(now - 6 * (long long)kDayInMs),
  3825. @(now - 7 * (long long)kDayInMs),
  3826. @(now - 8 * (long long)kDayInMs),
  3827. @(now - 2 * (long long)kWeekInMs),
  3828. @(now - 1 * (long long)kMonthInMs),
  3829. @(now - 2 * (long long)kMonthInMs),
  3830. ];
  3831. NSMutableArray<NSString *> *recipientIds = [thread.recipientIdentifiers mutableCopy];
  3832. [recipientIds removeObject:[TSAccountManager localNumber]];
  3833. NSString *recipientId = (recipientIds.count > 0 ? recipientIds.firstObject : @"+19174054215");
  3834. [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3835. for (NSNumber *timestamp in timestamps) {
  3836. NSString *randomText = [self randomText];
  3837. {
  3838. // Legit usage of SenderTimestamp to backdate incoming sent messages for Debug
  3839. TSIncomingMessage *message =
  3840. [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp.unsignedLongLongValue
  3841. inThread:thread
  3842. authorId:recipientId
  3843. sourceDeviceId:0
  3844. messageBody:randomText
  3845. attachmentIds:[NSMutableArray new]
  3846. expiresInSeconds:0
  3847. quotedMessage:nil
  3848. contactShare:nil
  3849. linkPreview:nil
  3850. serverTimestamp:nil
  3851. wasReceivedByUD:NO];
  3852. [message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
  3853. }
  3854. {
  3855. // MJK TODO - this might be the one place we actually use senderTimestamp
  3856. TSOutgoingMessage *message =
  3857. [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:timestamp.unsignedLongLongValue
  3858. inThread:thread
  3859. messageBody:randomText
  3860. attachmentIds:[NSMutableArray new]
  3861. expiresInSeconds:0
  3862. expireStartedAt:0
  3863. isVoiceMessage:NO
  3864. groupMetaMessage:TSGroupMetaMessageUnspecified
  3865. quotedMessage:nil
  3866. contactShare:nil
  3867. linkPreview:nil];
  3868. [message saveWithTransaction:transaction];
  3869. [message updateWithFakeMessageState:TSOutgoingMessageStateSent transaction:transaction];
  3870. [message updateWithSentRecipient:recipientId wasSentByUD:NO transaction:transaction];
  3871. [message updateWithDeliveredRecipient:recipientId deliveryTimestamp:timestamp transaction:transaction];
  3872. [message updateWithReadRecipientId:recipientId
  3873. readTimestamp:timestamp.unsignedLongLongValue
  3874. transaction:transaction];
  3875. }
  3876. }
  3877. }];
  3878. }
  3879. + (void)createDisappearingMessagesWhichFailedToStartInThread:(TSThread *)thread
  3880. {
  3881. uint64_t now = [NSDate ows_millisecondTimeStamp];
  3882. // MJK TODO - should be safe to remove this senderTimestamp
  3883. TSIncomingMessage *message = [[TSIncomingMessage alloc]
  3884. initIncomingMessageWithTimestamp:now
  3885. inThread:thread
  3886. authorId:thread.recipientIdentifiers.firstObject
  3887. sourceDeviceId:0
  3888. messageBody:[NSString
  3889. stringWithFormat:@"Should disappear 60s after %lu", (unsigned long)now]
  3890. attachmentIds:[NSMutableArray new]
  3891. expiresInSeconds:60
  3892. quotedMessage:nil
  3893. contactShare:nil
  3894. linkPreview:nil
  3895. serverTimestamp:nil
  3896. wasReceivedByUD:NO];
  3897. // private setter to avoid starting expire machinery.
  3898. message.read = YES;
  3899. [message save];
  3900. }
  3901. + (void)testLinkificationInThread:(TSThread *)thread
  3902. {
  3903. NSArray<NSString *> *strings = @[@"google.com",
  3904. @"foo.google.com",
  3905. @"https://foo.google.com",
  3906. @"https://foo.google.com/some/path.html",
  3907. @"http://кц.com",
  3908. @"кц.com",
  3909. @"http://asĸ.com",
  3910. @"кц.рф",
  3911. @"кц.рф/some/path",
  3912. @"https://кц.рф/some/path",
  3913. @"http://foo.кц.рф"];
  3914. [OWSPrimaryStorage.sharedManager.dbReadWriteConnection
  3915. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3916. for (NSString *string in strings) {
  3917. // DO NOT log these strings with the debugger attached.
  3918. // OWSLogInfo(@"%@", string);
  3919. {
  3920. [self createFakeIncomingMessage:thread
  3921. messageBody:string
  3922. fakeAssetLoader:nil
  3923. isAttachmentDownloaded:NO
  3924. quotedMessage:nil
  3925. transaction:transaction];
  3926. }
  3927. {
  3928. NSString *recipientId = @"+1323555555";
  3929. NSString *groupName = string;
  3930. NSMutableArray<NSString *> *recipientIds = [@[
  3931. recipientId,
  3932. [TSAccountManager localNumber],
  3933. ] mutableCopy];
  3934. NSData *groupId = [Randomness generateRandomBytes:kGroupIdLength];
  3935. TSGroupModel *groupModel =
  3936. [[TSGroupModel alloc] initWithTitle:groupName memberIds:recipientIds image:nil groupId:groupId];
  3937. TSGroupThread *groupThread =
  3938. [TSGroupThread getOrCreateThreadWithGroupModel:groupModel transaction:transaction];
  3939. OWSAssertDebug(groupThread);
  3940. }
  3941. }
  3942. }];
  3943. }
  3944. + (void)testIndicScriptsInThread:(TSThread *)thread
  3945. {
  3946. NSArray<NSString *> *strings = @[
  3947. @"\u0C1C\u0C4D\u0C1E\u200C\u0C3E",
  3948. @"\u09B8\u09CD\u09B0\u200C\u09C1",
  3949. @"non-crashing string",
  3950. ];
  3951. [OWSPrimaryStorage.sharedManager.dbReadWriteConnection
  3952. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3953. for (NSString *string in strings) {
  3954. // DO NOT log these strings with the debugger attached.
  3955. // OWSLogInfo(@"%@", string);
  3956. {
  3957. [self createFakeIncomingMessage:thread
  3958. messageBody:string
  3959. fakeAssetLoader:nil
  3960. isAttachmentDownloaded:NO
  3961. quotedMessage:nil
  3962. transaction:transaction];
  3963. }
  3964. {
  3965. NSString *recipientId = @"+19174054215";
  3966. NSString *groupName = string;
  3967. NSMutableArray<NSString *> *recipientIds = [@[
  3968. recipientId,
  3969. [TSAccountManager localNumber],
  3970. ] mutableCopy];
  3971. NSData *groupId = [Randomness generateRandomBytes:kGroupIdLength];
  3972. TSGroupModel *groupModel =
  3973. [[TSGroupModel alloc] initWithTitle:groupName memberIds:recipientIds image:nil groupId:groupId];
  3974. TSGroupThread *groupThread =
  3975. [TSGroupThread getOrCreateThreadWithGroupModel:groupModel transaction:transaction];
  3976. OWSAssertDebug(groupThread);
  3977. }
  3978. }
  3979. }];
  3980. }
  3981. + (void)testZalgoTextInThread:(TSThread *)thread
  3982. {
  3983. NSArray<NSString *> *strings = @[
  3984. @"Ṱ̴̤̺̣͚͚̭̰̤̮̑̓̀͂͘͡h̵̢̤͔̼̗̦̖̬͌̀͒̀͘i̴̮̤͎͎̝̖̻͓̅̆͆̓̎͘͡ͅŝ̡̡̳͔̓͗̾̀̇͒͘͢͢͡͡ ỉ̛̲̩̫̝͉̀̒͐͋̾͘͢͡͞s̶̨̫̞̜̹͛́̇͑̅̒̊̈ s̵͍̲̗̠̗͈̦̬̉̿͂̏̐͆̾͐͊̾ǫ̶͍̼̝̉͊̉͢͜͞͝ͅͅṁ̵̡̨̬̤̝͔̣̄̍̋͊̿̄͋̈ͅe̪̪̻̱͖͚͈̲̍̃͘͠͝ z̷̢̢̛̩̦̱̺̼͑́̉̾ą͕͎̠̮̹̱̓̔̓̈̈́̅̐͢l̵̨͚̜͉̟̜͉͎̃͆͆͒͑̍̈̚͜͞ğ͔̖̫̞͎͍̒̂́̒̿̽̆͟o̶̢̬͚̘̤̪͇̻̒̋̇̊̏͢͡͡͠ͅ t̡̛̥̦̪̮̅̓̑̈́̉̓̽͛͢͡ȩ̡̩͓͈̩͎͗̔͑̌̓͊͆͝x̫̦͓̤͓̘̝̪͊̆͌͊̽̃̏͒͘͘͢ẗ̶̢̨̛̰̯͕͔́̐͗͌͟͠.̷̩̼̼̩̞̘̪́͗̅͊̎̾̅̏̀̕͟ͅ",
  3985. @"This is some normal text",
  3986. ];
  3987. [OWSPrimaryStorage.sharedManager.dbReadWriteConnection
  3988. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  3989. for (NSString *string in strings) {
  3990. OWSLogInfo(@"sending zalgo");
  3991. {
  3992. [self createFakeIncomingMessage:thread
  3993. messageBody:string
  3994. fakeAssetLoader:nil
  3995. isAttachmentDownloaded:NO
  3996. quotedMessage:nil
  3997. transaction:transaction];
  3998. }
  3999. {
  4000. NSString *recipientId = @"+19174054215";
  4001. NSString *groupName = string;
  4002. NSMutableArray<NSString *> *recipientIds = [@[
  4003. recipientId,
  4004. [TSAccountManager localNumber],
  4005. ] mutableCopy];
  4006. NSData *groupId = [Randomness generateRandomBytes:kGroupIdLength];
  4007. TSGroupModel *groupModel =
  4008. [[TSGroupModel alloc] initWithTitle:groupName memberIds:recipientIds image:nil groupId:groupId];
  4009. TSGroupThread *groupThread =
  4010. [TSGroupThread getOrCreateThreadWithGroupModel:groupModel transaction:transaction];
  4011. OWSAssertDebug(groupThread);
  4012. }
  4013. }
  4014. }];
  4015. }
  4016. + (void)testDirectionalFilenamesInThread:(TSThread *)thread
  4017. {
  4018. NSMutableArray<NSString *> *filenames = [@[
  4019. @"a_test\u202Dabc.exe",
  4020. @"b_test\u202Eabc.exe",
  4021. @"c_testabc.exe",
  4022. ] mutableCopy];
  4023. __block void (^sendUnsafeFile)(void);
  4024. sendUnsafeFile = ^{
  4025. if (filenames.count < 1) {
  4026. return;
  4027. }
  4028. NSString *filename = filenames.lastObject;
  4029. [filenames removeLastObject];
  4030. NSString *utiType = (NSString *)kUTTypeData;
  4031. const NSUInteger kDataLength = 32;
  4032. DataSource *_Nullable dataSource =
  4033. [DataSourceValue dataSourceWithData:[self createRandomNSDataOfSize:kDataLength] utiType:utiType];
  4034. [dataSource setSourceFilename:filename];
  4035. SignalAttachment *attachment =
  4036. [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType imageQuality:TSImageQualityOriginal];
  4037. OWSAssertDebug(attachment);
  4038. if ([attachment hasError]) {
  4039. OWSLogError(@"attachment[%@]: %@", [attachment sourceFilename], [attachment errorName]);
  4040. [DDLog flushLog];
  4041. }
  4042. OWSAssertDebug(![attachment hasError]);
  4043. [self sendAttachment:attachment thread:thread messageBody:nil];
  4044. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  4045. sendUnsafeFile();
  4046. sendUnsafeFile = nil;
  4047. });
  4048. };
  4049. }
  4050. + (void)deleteAllMessagesInThread:(TSThread *)thread
  4051. {
  4052. [OWSPrimaryStorage.sharedManager.newDatabaseConnection
  4053. readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
  4054. [thread removeAllThreadInteractionsWithTransaction:transaction];
  4055. }];
  4056. }
  4057. #pragma mark - Utility
  4058. + (NSString *)actionLabelForHasCaption:(BOOL)hasCaption
  4059. outgoingMessageState:(TSOutgoingMessageState)outgoingMessageState
  4060. isDelivered:(BOOL)isDelivered
  4061. isRead:(BOOL)isRead
  4062. {
  4063. NSMutableString *label = [NSMutableString new];
  4064. if (hasCaption) {
  4065. [label appendString:@" 🔤"];
  4066. }
  4067. if (outgoingMessageState == TSOutgoingMessageStateFailed) {
  4068. [label appendString:@" (Unsent)"];
  4069. } else if (outgoingMessageState == TSOutgoingMessageStateSending) {
  4070. [label appendString:@" (Sending)"];
  4071. } else if (outgoingMessageState == TSOutgoingMessageStateSent) {
  4072. if (isRead) {
  4073. [label appendString:@" (Read)"];
  4074. } else if (isDelivered) {
  4075. [label appendString:@" (Delivered)"];
  4076. } else {
  4077. [label appendString:@" (Sent)"];
  4078. }
  4079. } else {
  4080. OWSFailDebug(@"unknown message state.");
  4081. }
  4082. return label;
  4083. }
  4084. + (TSOutgoingMessage *)createFakeOutgoingMessage:(TSThread *)thread
  4085. messageBody:(nullable NSString *)messageBody
  4086. fakeAssetLoader:(nullable DebugUIMessagesAssetLoader *)fakeAssetLoader
  4087. messageState:(TSOutgoingMessageState)messageState
  4088. isDelivered:(BOOL)isDelivered
  4089. isRead:(BOOL)isRead
  4090. quotedMessage:(nullable TSQuotedMessage *)quotedMessage
  4091. contactShare:(nullable OWSContact *)contactShare
  4092. linkPreview:(nullable OWSLinkPreview *)linkPreview
  4093. transaction:(YapDatabaseReadWriteTransaction *)transaction
  4094. {
  4095. OWSAssertDebug(thread);
  4096. // Seamlessly convert oversize text messages to oversize text attachments.
  4097. if ([messageBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) {
  4098. OWSAssertDebug(!fakeAssetLoader);
  4099. fakeAssetLoader = [DebugUIMessagesAssetLoader oversizeTextInstanceWithText:messageBody];
  4100. messageBody = nil;
  4101. }
  4102. TSAttachment *_Nullable attachment = nil;
  4103. if (fakeAssetLoader) {
  4104. attachment = [self createFakeAttachment:fakeAssetLoader isAttachmentDownloaded:YES transaction:transaction];
  4105. }
  4106. return [self createFakeOutgoingMessage:thread
  4107. messageBody:messageBody
  4108. attachmentId:attachment.uniqueId
  4109. filename:fakeAssetLoader.filename
  4110. messageState:messageState
  4111. isDelivered:isDelivered
  4112. isRead:isRead
  4113. isVoiceMessage:attachment.isVoiceMessage
  4114. quotedMessage:quotedMessage
  4115. contactShare:contactShare
  4116. linkPreview:linkPreview
  4117. transaction:transaction];
  4118. }
  4119. + (TSOutgoingMessage *)createFakeOutgoingMessage:(TSThread *)thread
  4120. messageBody:(nullable NSString *)messageBody
  4121. attachmentId:(nullable NSString *)attachmentId
  4122. filename:(nullable NSString *)filename
  4123. messageState:(TSOutgoingMessageState)messageState
  4124. isDelivered:(BOOL)isDelivered
  4125. isRead:(BOOL)isRead
  4126. isVoiceMessage:(BOOL)isVoiceMessage
  4127. quotedMessage:(nullable TSQuotedMessage *)quotedMessage
  4128. contactShare:(nullable OWSContact *)contactShare
  4129. linkPreview:(nullable OWSLinkPreview *)linkPreview
  4130. transaction:(YapDatabaseReadWriteTransaction *)transaction
  4131. {
  4132. OWSAssertDebug(thread);
  4133. OWSAssertDebug(transaction);
  4134. OWSAssertDebug(messageBody.length > 0 || attachmentId.length > 0 || contactShare);
  4135. NSMutableArray<NSString *> *attachmentIds = [NSMutableArray new];
  4136. if (attachmentId) {
  4137. [attachmentIds addObject:attachmentId];
  4138. }
  4139. // MJK TODO - remove senderTimestamp
  4140. TSOutgoingMessage *message =
  4141. [[TSOutgoingMessage alloc] initOutgoingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
  4142. inThread:thread
  4143. messageBody:messageBody
  4144. attachmentIds:attachmentIds
  4145. expiresInSeconds:0
  4146. expireStartedAt:0
  4147. isVoiceMessage:isVoiceMessage
  4148. groupMetaMessage:TSGroupMetaMessageUnspecified
  4149. quotedMessage:quotedMessage
  4150. contactShare:contactShare
  4151. linkPreview:linkPreview];
  4152. if (attachmentId.length > 0 && filename.length > 0) {
  4153. message.attachmentFilenameMap[attachmentId] = filename;
  4154. }
  4155. [message saveWithTransaction:transaction];
  4156. [message updateWithFakeMessageState:messageState transaction:transaction];
  4157. if (isDelivered) {
  4158. NSString *_Nullable recipientId = thread.recipientIdentifiers.lastObject;
  4159. OWSAssertDebug(recipientId.length > 0);
  4160. [message updateWithDeliveredRecipient:recipientId
  4161. deliveryTimestamp:@([NSDate ows_millisecondTimeStamp])
  4162. transaction:transaction];
  4163. }
  4164. if (isRead) {
  4165. NSString *_Nullable recipientId = thread.recipientIdentifiers.lastObject;
  4166. OWSAssertDebug(recipientId.length > 0);
  4167. [message updateWithReadRecipientId:recipientId
  4168. readTimestamp:[NSDate ows_millisecondTimeStamp]
  4169. transaction:transaction];
  4170. }
  4171. return message;
  4172. }
  4173. + (TSIncomingMessage *)createFakeIncomingMessage:(TSThread *)thread
  4174. messageBody:(nullable NSString *)messageBody
  4175. fakeAssetLoader:(nullable DebugUIMessagesAssetLoader *)fakeAssetLoader
  4176. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  4177. quotedMessage:(nullable TSQuotedMessage *)quotedMessage
  4178. transaction:(YapDatabaseReadWriteTransaction *)transaction
  4179. {
  4180. OWSAssertDebug(thread);
  4181. // Seamlessly convert oversize text messages to oversize text attachments.
  4182. if ([messageBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= kOversizeTextMessageSizeThreshold) {
  4183. OWSAssertDebug(!fakeAssetLoader);
  4184. fakeAssetLoader = [DebugUIMessagesAssetLoader oversizeTextInstanceWithText:messageBody];
  4185. messageBody = nil;
  4186. }
  4187. TSAttachment *_Nullable attachment = nil;
  4188. if (fakeAssetLoader) {
  4189. attachment = [self createFakeAttachment:fakeAssetLoader
  4190. isAttachmentDownloaded:isAttachmentDownloaded
  4191. transaction:transaction];
  4192. }
  4193. return [self createFakeIncomingMessage:thread
  4194. messageBody:messageBody
  4195. attachmentId:attachment.uniqueId
  4196. filename:fakeAssetLoader.filename
  4197. isAttachmentDownloaded:isAttachmentDownloaded
  4198. quotedMessage:quotedMessage
  4199. transaction:transaction];
  4200. }
  4201. + (TSIncomingMessage *)createFakeIncomingMessage:(TSThread *)thread
  4202. messageBody:(nullable NSString *)messageBody
  4203. attachmentId:(nullable NSString *)attachmentId
  4204. filename:(nullable NSString *)filename
  4205. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  4206. quotedMessage:(nullable TSQuotedMessage *)quotedMessage
  4207. transaction:(YapDatabaseReadWriteTransaction *)transaction
  4208. {
  4209. OWSAssertDebug(thread);
  4210. OWSAssertDebug(transaction);
  4211. OWSAssertDebug(messageBody.length > 0 || attachmentId.length > 0);
  4212. NSMutableArray<NSString *> *attachmentIds = [NSMutableArray new];
  4213. if (attachmentId) {
  4214. [attachmentIds addObject:attachmentId];
  4215. }
  4216. // // Random time within last n years. Helpful for filling out a media gallery over time.
  4217. // double yearsMillis = 4.0 * kYearsInMs;
  4218. // uint64_t millisAgo = (uint64_t)(((double)arc4random() / ((double)0xffffffff)) * yearsMillis);
  4219. // uint64_t timestamp = [NSDate ows_millisecondTimeStamp] - millisAgo;
  4220. // MJK TODO - should be safe to remove this senderTimestamp
  4221. TSIncomingMessage *message =
  4222. [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:[NSDate ows_millisecondTimeStamp]
  4223. inThread:thread
  4224. authorId:@"+19174054215"
  4225. sourceDeviceId:0
  4226. messageBody:messageBody
  4227. attachmentIds:attachmentIds
  4228. expiresInSeconds:0
  4229. quotedMessage:quotedMessage
  4230. contactShare:nil
  4231. linkPreview:nil
  4232. serverTimestamp:nil
  4233. wasReceivedByUD:NO];
  4234. [message markAsReadNowWithSendReadReceipt:NO transaction:transaction];
  4235. return message;
  4236. }
  4237. + (TSAttachment *)createFakeAttachment:(DebugUIMessagesAssetLoader *)fakeAssetLoader
  4238. isAttachmentDownloaded:(BOOL)isAttachmentDownloaded
  4239. transaction:(YapDatabaseReadWriteTransaction *)transaction
  4240. {
  4241. OWSAssertDebug(fakeAssetLoader);
  4242. OWSAssertDebug(fakeAssetLoader.filePath);
  4243. OWSAssertDebug(transaction);
  4244. if (isAttachmentDownloaded) {
  4245. DataSource *dataSource =
  4246. [DataSourcePath dataSourceWithFilePath:fakeAssetLoader.filePath shouldDeleteOnDeallocation:NO];
  4247. NSString *filename = dataSource.sourceFilename;
  4248. // To support "fake missing" attachments, we sometimes lie about the
  4249. // length of the data.
  4250. UInt32 nominalDataLength = (UInt32)MAX((NSUInteger)1, dataSource.dataLength);
  4251. TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:fakeAssetLoader.mimeType
  4252. byteCount:nominalDataLength
  4253. sourceFilename:filename
  4254. caption:nil
  4255. albumMessageId:nil];
  4256. NSError *error;
  4257. BOOL success = [attachmentStream writeData:dataSource.data error:&error];
  4258. OWSAssertDebug(success && !error);
  4259. [attachmentStream saveWithTransaction:transaction];
  4260. return attachmentStream;
  4261. } else {
  4262. UInt32 filesize = 64;
  4263. TSAttachmentPointer *attachmentPointer =
  4264. [[TSAttachmentPointer alloc] initWithServerId:237391539706350548
  4265. key:[self createRandomNSDataOfSize:filesize]
  4266. digest:nil
  4267. byteCount:filesize
  4268. contentType:fakeAssetLoader.mimeType
  4269. sourceFilename:fakeAssetLoader.filename
  4270. caption:nil
  4271. albumMessageId:nil
  4272. attachmentType:TSAttachmentTypeDefault
  4273. mediaSize:CGSizeZero];
  4274. attachmentPointer.state = TSAttachmentPointerStateFailed;
  4275. [attachmentPointer saveWithTransaction:transaction];
  4276. return attachmentPointer;
  4277. }
  4278. }
  4279. + (void)sendMediaAlbumInThread:(TSThread *)thread
  4280. {
  4281. OWSLogInfo(@"");
  4282. const uint32_t kMinImageCount = 2;
  4283. const uint32_t kMaxImageCount = 10;
  4284. uint32_t imageCount = kMinImageCount + arc4random_uniform(kMaxImageCount - kMinImageCount);
  4285. NSString *_Nullable messageBody = (arc4random_uniform(2) > 0 ? @"This is the media gallery title..." : nil);
  4286. [self sendMediaAlbumInThread:thread imageCount:imageCount messageBody:messageBody];
  4287. }
  4288. + (void)sendExemplaryMediaGalleriesInThread:(TSThread *)thread
  4289. {
  4290. OWSLogInfo(@"");
  4291. [self sendMediaAlbumInThread:thread imageCount:2 messageBody:nil];
  4292. [self sendMediaAlbumInThread:thread imageCount:3 messageBody:nil];
  4293. [self sendMediaAlbumInThread:thread imageCount:4 messageBody:nil];
  4294. [self sendMediaAlbumInThread:thread imageCount:5 messageBody:nil];
  4295. [self sendMediaAlbumInThread:thread imageCount:6 messageBody:nil];
  4296. [self sendMediaAlbumInThread:thread imageCount:7 messageBody:nil];
  4297. NSString *messageBody = @"This is the media gallery title...";
  4298. [self sendMediaAlbumInThread:thread imageCount:2 messageBody:messageBody];
  4299. [self sendMediaAlbumInThread:thread imageCount:3 messageBody:messageBody];
  4300. [self sendMediaAlbumInThread:thread imageCount:4 messageBody:messageBody];
  4301. [self sendMediaAlbumInThread:thread imageCount:5 messageBody:messageBody];
  4302. [self sendMediaAlbumInThread:thread imageCount:6 messageBody:messageBody];
  4303. [self sendMediaAlbumInThread:thread imageCount:7 messageBody:messageBody];
  4304. }
  4305. + (void)sendMediaAlbumInThread:(TSThread *)thread
  4306. imageCount:(uint32_t)imageCount
  4307. messageBody:(nullable NSString *)messageBody
  4308. fakeAssetLoaders:(NSArray<DebugUIMessagesAssetLoader *> *)fakeAssetLoaders
  4309. {
  4310. OWSAssertDebug(imageCount > 0);
  4311. OWSLogInfo(@"");
  4312. NSMutableArray<SignalAttachment *> *attachments = [NSMutableArray new];
  4313. for (uint32_t i = 0; i < imageCount; i++) {
  4314. DebugUIMessagesAssetLoader *fakeAssetLoader
  4315. = fakeAssetLoaders[arc4random_uniform((uint32_t)fakeAssetLoaders.count)];
  4316. OWSAssertDebug([NSFileManager.defaultManager fileExistsAtPath:fakeAssetLoader.filePath]);
  4317. DataSource *dataSource =
  4318. [DataSourcePath dataSourceWithFilePath:fakeAssetLoader.filePath shouldDeleteOnDeallocation:NO];
  4319. SignalAttachment *attachment =
  4320. [SignalAttachment attachmentWithDataSource:dataSource
  4321. dataUTI:[MIMETypeUtil utiTypeForMIMEType:fakeAssetLoader.mimeType]
  4322. imageQuality:TSImageQualityOriginal];
  4323. if (arc4random_uniform(2) == 0) {
  4324. attachment.captionText = [self randomText];
  4325. }
  4326. [attachments addObject:attachment];
  4327. }
  4328. [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
  4329. TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithText:messageBody
  4330. mediaAttachments:attachments
  4331. inThread:thread
  4332. quotedReplyModel:nil
  4333. linkPreviewDraft:nil
  4334. transaction:transaction];
  4335. OWSLogDebug(@"timestamp: %llu.", message.timestamp);
  4336. }];
  4337. }
  4338. + (void)sendMediaAlbumInThread:(TSThread *)thread
  4339. imageCount:(uint32_t)imageCount
  4340. messageBody:(nullable NSString *)messageBody
  4341. {
  4342. OWSAssertDebug(thread);
  4343. NSArray<DebugUIMessagesAssetLoader *> *fakeAssetLoaders = @[
  4344. [DebugUIMessagesAssetLoader jpegInstance],
  4345. [DebugUIMessagesAssetLoader largePngInstance],
  4346. [DebugUIMessagesAssetLoader tinyPngInstance],
  4347. [DebugUIMessagesAssetLoader gifInstance],
  4348. [DebugUIMessagesAssetLoader mp4Instance],
  4349. ];
  4350. [DebugUIMessagesAssetLoader prepareAssetLoaders:fakeAssetLoaders
  4351. success:^{
  4352. [self sendMediaAlbumInThread:thread
  4353. imageCount:imageCount
  4354. messageBody:messageBody
  4355. fakeAssetLoaders:fakeAssetLoaders];
  4356. }
  4357. failure:^{
  4358. OWSLogError(@"Could not prepare fake asset loaders.");
  4359. }];
  4360. }
  4361. #endif
  4362. @end
  4363. NS_ASSUME_NONNULL_END