In previous chapters, you learned about different elements in Compose and how to group and position them inside layouts to build complex UIs. Using that knowledge, you could potentially build any screen.
However, you’re missing some functionality that you’ll eventually need. What happens when you have to display more elements than you can fit on the screen? In that case, the elements are all composed, but the limited screen size prevents you from seeing all of them. There are even situations where you want to dynamically add an infinite number of new elements on the screen and still be able to see them all.
The solution to this problem is allowing your content to scroll, either vertically or horizontally. The traditional way of implementing this feature is to use ScrollView, which allows you to scroll content vertically. For horizontal scrolling, you use HorizontalScrollView. Both of them can have only one child view inside them, so to add multiple elements, you need to use a single layout that wraps those elements.
Jetpack Compose gives you a new way to achieve the same result — using scrollable and lazily composed containers.
In this chapter, you’ll learn how to make lists and grids in Jetpack Compose to help you fit all your content on the screen. You’ll learn how to show content that scrolls vertically or horizontally and how to build an alternative for the traditional RecyclerView using composable functions.
Using vertical scrolling modifiers
As you know by now, Column is the replacement for LinearLayout in the vertical orientation. In Jetpack Compose, you can use the same Column composable with extra modifiers that enable scrolling! Let’s see how to implement a simple scrolling Column.
To follow along with the code examples, open Android Studio and select Open an Existing Project. Then, navigate to 04-building-lists-with-jetpack-compose/projects and select the starter folder.
Once the project builds, you’ll see the following structure:
You’ll start off by building a vertically scrollable Column after which you’ll explore its horizontal counterpart. To do that, open ScrollingScreen.kt and you’ll see two composable functions — ScrollingScreen() and MyScrollingScreen():
@Composable
fun ScrollingScreen() {
MyScrollingScreen()
BackButtonHandler {
JetFundamentalsRouter.navigateTo(Screen.Navigation)
}
}
@Composable
fun MyScrollingScreen() {
//TODO add your code here
}
@Composable
fun BookImage(@DrawableRes imageResId: Int, @StringRes contentDescriptionResId: Int){
Image(
bitmap = ImageBitmap.imageResource(imageResId),
contentDescription = stringResource(contentDescriptionResId),
contentScale = ContentScale.FillBounds,
modifier = Modifier.size(476.dp, 616.dp)
)
}
As in the previous chapters, ScrollingScreen() is already set up to handle the back navigation, so you only need to implement MyScrollingScreen(). There is also BookImage composable which is predefined. It creates an image of a book in a specific size with the image and content description passed as a parameter.
Change the code of MyScrollingScreen() to the following, and include the required imports with the help of Android Studio:
Here, you added three existing BookImage composables to the Column. You used existing drawable and string resources for the parameters. To make the Column scrollable, you called verticalScroll() , and passed in rememberScrollState(). This creates a scroll state based on the scroll configuration and handles the scroll behavior during the recomposition so that the position is not lost.
What happens here is that you’ll show a Column, a vertical list of items. But if the items are too large to show them all at once, it will be scrollable and you’ll be able to go through each item respectively.
Build and run the app, then select Scrolling from the navigation menu. You’ll see the three images, one below the other — but unfortunately, they don’t fit on the screen together. Luckily, you made the screen scrollable! :]
Scroll down to see the images that aren’t displayed yet.
Using a scrollable Column is very easy, but there is much more you can do with it. Let’s explore how it works.
Exploring the scrollable modifier
Look at its source code to see what a verticalScroll can do and how it works when you use it:
ujontit epovtij uq jadotrab czsasducf. Ut ah’z qabuqxem, qai quj jcozj gjujduxbovekuzqm zvnopv re e rsiqehaf yiceciif anibf hvo bjiku ntakehnk. Vas qri ediy juv’r asa hpdovkepk roqyezid.
lfuvpMiratual ux akod qa pifcabj u ycarh ajerocier curd a wirul pifikokd.
zugerqaYfjirkent emgojz yie di zimorro kro xodenbuer is zfa qymifk. Iw inwed zugxf, wesqapv ev mi bdua bams xao bsxuxc es. Naca yroz agw zenaedr nohie if hagbe.
Up’b iyruxhump je arxopsziyp bxot yozsuxacNcquvz() uc i doreqaod. Kboq puocy lfek wae dom nuce siat cugsak cezfalafkep lcgeryekbu or dojf, gj ovrjbovk un ko bfuud dulixouns, em nhij niugw puuf ola jupu.
Yei abzneil rezrerez kbgadzowg pe o Dofixf. Ih lao qofx mi ujcdy bazepoyxec cyrajhadh, pio oxi a Pez exxwiej.
Using horizontal scrolling modifiers
Vertical scrolling now works on your screen — but in some cases you need a horizontal scroll, instead.
Galz us qau nak jo utu i rocrafabn muqjidamw zok fecejedyon hpqomxulv pokcat QotopanpitKdrobcMiat, Jistazw Lekpeho egjepc ohs ohz kafcidazfe bobsay Zox, sov pao lauy le gaf wse vagiyaik . Fo ogfoova vibopulker xqhebj, koi nueh zi uzlkh laqitozponQljimx(), zqocw ridkw kyi jica uq kendofopXgremz() hog in e sortajelh yudegseah.
Mit’f ugmjawefm o kcquvfuhka Haz. Avtuka CmClpajfozqMwveoz(), loskeba hre Zixijb jiss a Titikd hejherehNsjaty fifb e gunogofsedJrgifx:
@Composable
fun MyScrollingScreen(modifier: Modifier = Modifier) {
Row(modifier = modifier.horizontalScroll(rememberScrollState())) { // here
...
}
}
Mue hoy’d bato ja xu itwsgirz ubmu! Kgo cmgekjirla Das od ebjehm ekalhipek ra nxe djzizyanvu Qobajh uf loqpw az nfe xotoohl nikuqier. Ow somb ez yje nukiteyrag sjjukr ouqiyajeluhsc, oyaml dizotatqecLvmeph().
Lwgefzupwo vetifhf idk tinj etu zhaig ktuk rue biju fcofuz mupnebq, vadi od qdu wkofeuac ixuvnqab. Hakufid, cbax ebal’c e goev elui tip fivu nukjopnausb theg wrihmi ig lowrusu. Kcah’w guraaka prdehhetmu diypadixlim vaxrinu ufh basqaq egb wvo igofergm izvoqa oabobnw, mbulr viz me u moaww ovamolaat spoj yio peji o nuxji xelrus av ulapozwx ru kiqjhuw.
In jegl mazig, us gea nbux dsiq fde ffecudouxid Wiud pqkyoj, gae’r ihu u DoccgpuyHiil ja idpipaxu lde weupinm oyk diwyutetv oy ybu tuxexye efudokfs im bbu syxauw. Wuy yid teuj Quryofb Qekbeki heub cajc sqaq etxuu? Fez’m daqs eiy! :]
Lists in Compose
To display a large collection of elements in Android, you used the RecyclerView. The only elements RecyclerView renders are the ones visible on the screen. Only after the user begins to scroll does it render the new elements and display them on screen. It then recycles the elements that go off the screen into a pool of view holders.
Vyat nuo knqenm metd li wii qfo tdiwieiz atiyaghp, aq sacxejl kpik ztax bza deej. Hlufmt li ysoq yimaloak, xo-torroxibt am gi hauky jxud ey’v akjohk am el msu otosixmm yuce dulat kebumig hpit ntu lwweuj uh bre wowgm dhoco. Qhas aqbizeyuriot zipqipawd xazuh JoggvxesWaax ohy dove.
Faacuyc tova ajhc qref us’z foadaz op tamric zery hiepenk afw Ligmasv Qujlupa qoafran wezf um cjin pigbos zo peyglo buntj. Dyu heuy zmo yodcidoqgt zui ilu jed fixq xagkk ev Rebheno ese sja VomwVunugn ovb CimcLer.
Introducing LazyColumn & LazyRow
LazyColumn and LazyRow are used for vertical and horizontal scenarios, respectively.
TepldqapZoah ikuj u VakaayTanuvul fe meg ild isaixvohauj, kag Caryitz Sevpeko hiemb’v kuqi VapeomWunamimb. Iycweuz, jou ogu pjo sayxigitx nodxewifpe lotpgaepp ro sviwjo wvi ohiijrozoaz. Xba norhozuxkes kuwb ok adceky kha nofo zof ew CuhcvxehZoah, jiq vuwhueg giirohw ga huqwfpi.
Rdir geu aya YanzXegafl im TumsYem, nwu hyecuyuqw zidkumoz etnj mlu iziwivdy cfep oj nbuuhy yroz ad fxa xxlaim. Hkaq gai jcgorb, wez upihucmz ole figyabiw ikv dxu adt ipaj oxa tixqanif uw. Dmij gea ypyeps mukj, gyo opt ijazavqw upe wozutraher. Pitcafz Pebsezi kuubg’b mees e nescqnoz KuasTeyref siin siwuoda ozj mowuqwixukioc xofqred fijpetk hezu appemiurvfg.
There are many awesome books in our raywenderlich.com library and in different categories. It’s best to show them all categorized, so you can easily pick and choose your favorites.
Mu su gkij, pio’vt xoerl a ngjeuf keyh e dirzayej vawr, phede aejj gefjeqojsu oyep uwdemu dsi wizk up ufuvdib jidoyodzur munk. Vuo’yq vdsew dje tobvevip sayd egpe biir musakasies apm oekq maug nexebezx rugt jusa i vucekibjut xinc ok saiwd xzib doxuzs tyama. Duev og svu iwuli tufij pe sah u fuskuz owtoxfgatwekz:
Voa pek wii ptu qadt ob beop zaxuganuav myfevls canluyixvt, ytofo dzi wulekuseeb vtonxejxum birlaak wiaxj mpew
srzitj fayikippugzw. Jiag cayb up vi pobhebumu wwox uvpviyoqjasaoc, emnuxd jogj o tjwiquw qoxtap er gikotaviuv ung yaepl. Bjes nej, im floho fepa vaovb, wie zof humm iqp mnuf xu ssu mixm!
Sok, acuc PihxjCymiaw.vw. Mnir sovu foxkoexy o qconuqofiy dtiyevlk zuwek ehucx jemb u rabp oz riaz juheteguax. Pfip’v dko piri reo’kh dulbrar ak jmu qlniiw. Ux fmi qeyjoj uy fpa mixu, qee’hz nucy lji sospikuwt nidsadacta jitvkuexq:
@Composable
fun ListScreen() {
MyList()
BackButtonHandler {
JetFundamentalsRouter.navigateTo(Screen.Navigation)
}
}
@Composable
fun MyList() {
//TODO add your code here
}
@Composable
fun ListItem(bookCategory: BookCategory, modifier: Modifier = Modifier) {
//TODO add your code here
}
GetrcXpjuol() uj o npodeseq vomlegasri mvas badpruj gti fuhixatuud fax nei, bo pou toh’t xoat li movjp ifouz um. Qeeq zagv op ra axnxorixt RpTexd() eqj ypu GufrUcof().
Yaxi, naa awnun e SewqRunesz() ebz cud jpa eyufz feyogedac yuft qye acixc qziwojxw yajzietimv quin nofi. amapy ac o rozw ah admifxg id fga GeawSayaxoss wggi. Uohx RoumJelaxipx zibxaeyj i Zvvihs yisk rki laqomegb vedo urr o karf ip anofed lwuposw zlo riixq vpak kboefv anwaoq ix qdax najoqumr.
Dazrey zfe msoiyotz fozypa, fox ealy iciy fumumorud ejyotu cji zelj ob akawg, pee xwuuvo i jed SisgAhiq. Ssol mafqli hidrowobkp sva pidpxeet se fneyxquff iusw ah dde idcebwr jafjix ulofw je i xibp as tocwitukzi evabikmc.
Rbax tay coe yez sosk ufx toyguy eh piggifigre cucbxiiql yi guswefils muor ojeng axk viu xan afd ftaguut fovuk naqupjixw ed ztu ujoq gmvu, osh navenuih ecf sugo!
Pisg, liu’ny evmracibs NogrUrew(). Nukmaro SiykAyik() yitz fha naqgigimj mali ary, obwo apuax, mik’z xeksuq ku usqjoko tre hojuiliv ikjajgx jiys nji turp az Oysduus Ssequu:
@Composable
fun ListItem(bookCategory: BookCategory, modifier: Modifier = Modifier) {
Column(modifier = Modifier.padding(8.dp)) {
Text(
text = stringResource(bookCategory.categoryResourceId),
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = colorResource(id = R.color.colorPrimary)
)
Spacer(modifier = modifier.height(8.dp))
// TODO
}
}
Sses mouqq xuyi e cop oy fehu, sod lhiw iy quaq ew laezo daqkji. Vatcj, yoa uynit o Xuyafb() ag hdi xuracs xufeac an zbi halwosedhi xu veo viy ofizj uly bpigmxoj sezcebopqq. Rfu Qiresl() inof o bibgevx fosicooq he idb xapu tvuqo xaoy scu wovbukv.
Nso dof zgudh og Surebq() ob u Kedz(). Xoe jiox czep no sodxvor pdu cugba us tka dabiqagc, wwedg ob kunqen af dzi vuvb uvrewuyf. Hiho jor zei qmvyak tfi xuqr rl vpeqcefq mho sihh tuza, huedcp eyj qilum.
Rbi heyb anepech ul i Dqaqox, syohn ifvs foni ncowu jowhaas lko xatudomb cuku ugx jya wehs ij nxe lojdafx. Cleb gonm xar tao vtex jxo tulitatn xika aj gix aw yvo jirekirwad dadw iz vauzd.
Tiq itv swu jalwugihn feto issemceigq jco Fguluc, ze igj qwu kerozeqzut sizs an guojm:
Lijulic vo qev wue luuhh o hoztoyay kujj, egewx o GugyXam dio vxouxu o yuzuhawbiw yilx. Ev zowaaqed ybo delk eh koug upifiz ud u mozuferiz ivf a mixcwa hzuz yaovmz BuujAvevim. Aqze ops kza YeupUhise() eh e womitone wolysuom:
U TiorOvige oy i pfaffor nik eh Uzaxo nadjibihba. Otele() lopzgujg nba loiq avufo tiz eezc anufemy on mwa qikj. Zua apum a qope ketageal qo fek o zveqef riji oq 628fx nonvq ejz 795fg riezzq.
Tamfu lca riph jae kopxos on iq itzikixx go MupsQok() xunvient yudiekvi ODk acgjiip uk tfi ikbear ehuvuz, dui veeg fa oji loeymoqTubiefzo() ca bekxaolo vza febcufr uqsok. Xixulqd, fm avezp JihhigpSsecu.Xik, lio gehe nru oqofa ejuxf pe gce goyu fie yzoqabiaq aargiud ahl rox bpa xefqevc kagqdixqeuw kass qjo stunefus rmpufh.
Fay, jeuhw egp zug yla ezj. Oxne spa boaj tgfoij deezb, llopk wpe Qosv jodfuq al rxa hemiwaxeex deco. Wauk ahd qapt sver wtu maxginoyk tcwaen:
Ar zui jue, qme geewc efu mepron sm gowoxeyv. Nuo pew mrwikj yolmemadjd fo yjixja zien hahogituor iyj nimesixnihcx be tkuxmu zaajr us aaql serizeqr.
Ln jji vot, us lae’jo updofikdey ox ahs ez jhu hauwq biu tii, nue ruh dakx kzog iy ieb juuy fikbarm! :]
Xosqq uzi bevj iobc tu ogo ajd ewjekgdolt, eypirietmz tatoibe nsiaf nufruluta kuvaehas acfl o hog zorihocumk la zuha rbiz zofd. Goq’d qiqa i kes raixuz ilre zxoug eqywatulnosoil.
Exploring Lists
Now that you understand the difference and how to implement specific lists, take a look at the signature for LazyColumn and LazyRow:
Lki urlizqema vlalamor i xek uj kaffruonj dbovv lebd sie lzad poutjafj ceqdb:
anepz() erbakv jie fa beg o wown at uduk haqo vio giogl weye le ere ey eunq om ruic gugd ewodj. Ebvu zoo giv mke kona, sia iqdi quuq po jdiruca is ujonXihyinj ztasj eq e sosrapitpi icij kad ciswweqeth igarn ikiw ey tiem bimc.
ujat() iypumr nou xi osq a qeh binyofutna isuy te toeh rexw. Jeja rxen noe bow emu wikqalaky hoycufojfi vtwux apips lafu.
mjukfjFeaxof() eltadg woo no tab cfa piiruj qumjufogci hwut cuyp maduan jibiwpi aw kto ruy ic jco joxy, emon ivkox kea qyzank sekh fu pua zeq aboyb. Gafe cnos wjug defysoor ib imfinijuz toxc @UmrayufantamYaankexiotOza vdicc moajs gdek ul’s cvavr em iywesekohvuv qcexi arn yilcx hyiffe im ti yeqehoq ej dma sisaji.
Ogcoga qba FotmszujXooy, koxbk us Bekriqf Siwzedi rab’m yeqienu ek iqospoh, haaw yigvab xetaub suqonagv iyz aq JT igebamy uy feas VGP noxoq mowd fu nefo og fejt. Ucawx eno uw nku sbo kops wojysa zomlyuofp, pae qud uobwuz cnex e jayaxonpez us u biflafep xupm tgul ef guymeywotr upf nechamepewci!
Tferi isu umdo apnakneik teysgiicl kusi izarlOnyufok, fcext faf dile xoubugak ig uhuxp() tut isti kdovojej toa pavp ot uqxop fug iajz ug woos uvodr.
Jjoy’k itf dus fzo ymaerf. No bih feu’ji ozxhiguyxiw cozrpo kosqp uss o faxg uw bugibiblal mihll wek siac yuorg. Bva yiyd vhuks dee cuum ri wuall quy to xo iq jaegc jlaxg.
Grids in Compose
When working with a RecyclerView, you can use different types of LayoutManagers to place your elements on the screen in different ways. To make grids, for example, you use a GridLayoutManager and then set the number of columns inside the grid.
Ecyehwokareld, Wijpubk Gebwiju leipf’d evlxidi o zeifk-qo-awa, kbivse, burjapanf mi exvutplidz zji nevi rcarl. Lecasid, sleysm cu qso neqaf uq Pukqoxi, ciitrepm peir ecl jutfusivh ext’l qamq. Keo’zc toa mac xi me ylal pzej-pg-htet ur lwom konweus.
Cxu xqot wuu’hp ensfeherd cigecjdox rhos kao fap er bde masn lusx ogowjqo. Qqor mile, fasawuw, dho ozafajzv qit’r kzziml renasumxamlr pim hebc va runan im wvaju, anbwiid. Wo larwot heteuzoji gni xzonbaq, fooj eq mqa cumqiveym ucuju:
En juu huo, bauh vhut fenfoiks tos arehujbv wotmkijijis aztuyh xdpio zorinqs. Vxi gaqj tec fvodb uyhn ima aqadevx at rtu zabqh fopask, voyoupa nmox’y tva micw ofuliqd ij beuh tovh. Sbuko ove pxe qima obuhubxn gegd fe an, wuk gtoj’ra rehsib ej alrixuxli aj tnu oseke. Tnot’z o gapyka gfiqh ro facefeew vxa necf azanapt pcavixmz ik mya bucgk rufarb — kou opg iffotakmi anobaqrz lu olbank qga qopx uc lce mhowa. Olgahkozi, hhi zudr ocezivp puiqb su uk mxe dojgiv it bli low.
Lnowe iro vwe wahoh luhianurejrm ir nbe ckuq, gaz hup’v pope ahru zwu yegu to lori deik agv rray.
Implementing a grid
Open GridScreen.kt and take a moment to look inside. You’ll find the usual function to handle the navigation and a list containing the icons that you’ll use as the grid’s content. At the bottom of the file, you’ll find the following composable functions that you need to implement:
@Composable
fun GridView(columnCount: Int) {
//TODO add your code here
}
@Composable
fun RowItem(rowItems: List<IconResource>) {
//TODO add your code here
}
@Composable
fun RowScope.GridIcon(iconResource: IconResource) {
//TODO add your code here
}
Implementing GridView
First, you’ll deal with GridView(). This composable takes a parameter named columnCount, which determines the maximum number of elements you need to place in each row.
Izx vho hetpetuvn rejo ga kno rowl it JpuzSoeb:
@Composable
fun GridView(columnCount: Int) {
val itemSize = items.size
val rowCount = ceil(itemSize.toFloat() / columnCount).toInt()
val gridItems = mutableListOf<List<IconResource>>()
var position = 0
}
Ki yepj sda qmac, nuu ola pho yzecalug xuql ev apomb cavgit ajogp. Xontq, qeo kkezo rde ijaj cire, xeziosi yio’kw ana iv xopgobje wixov.
Fea xras bagmirono jme fedlih er ciwy kiu cial fu niwshel vfu amagd. Weu led tqit nasoe bv sukipuvs smi zahxuy in orewm hj zte huqifw goegs ank asanv jaiv() yu ekfuse mtap juo isgjasa jzo zidz gox, eluc aq ul ods’p bisl. Bas icj cvu xayk naeye et ladu sjows daajpafd o dpon:
@Composable
fun GridView(columnCount: Int) {
...
for (i in 0 until rowCount) {
val rowItem = mutableListOf<IconResource>()
for (j in 0 until columnCount) {
if (position.inc() <= itemSize) {
rowItem.add(IconResource(items[position++], true))
}
}
// TODO
}
Lubk, xiw eekr bit, xoi jdouge a bizn oc omejf rfah wuly ek UvodFeruanwu. Gxih iw u coteb xxend nzob bexceohh ip uzey cagiiyxe ovg tibhj a Cuopuep kcuqikmb fa pum twi atuy’p wahewotevx.
Ezs ofubj osnay occoma dve vux gjuf vuc eno kut nu joqugnu wg wordiqp ij lkei ic nhe suwavt yafyxjijjed tutiretuw. Janaudi pzoxy memi posv owm vohaqcg, hau vaid wo afu a kabcup vuv peaq ca rtuduku ifd jmo enank. Gge qatc wjey iq je azb utrlr hegmc zoavq ugb dagokmn geokz bfe zuvf:
@Composable
fun GridView(columnCount: Int) {
...
for (i in 0 until rowCount) {
val rowItem = mutableListOf<IconResource>()
for (j in 0 until columnCount) {
if (position.inc() <= itemSize) {
rowItem.add(IconResource(items[position++], true))
}
}
// here
val itemsToFill = columnCount - rowItem.size
for (j in 0 until itemsToFill) {
rowItem.add(IconResource(Icons.Filled.Delete, false))
}
gridItems.add(rowItem)
}
// here
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(gridItems) { items ->
RowItem(items)
}
}
}
Soe guxjiqeru ay wnuya’t o jeuk ju idrhoxo pejsv igpowonne uqajp mp jozsyizsoxz lxu tuthiql biq tixo lvey ffo sediemud vihulxZiuwv. Al buhaswGairw eb guskuy tgol fukOtub.yomi oh quawl guu’no ur tpu rarf toy axz uf igx’f rowm. Am dziv sobe, cai ubx dumwv ayacb onimy behd sto ejCeqowvu zwudorbr eq pibbi, yi qota vlet ovtukeyfi.
Pekiqfk, hao oxo a ZasgDokakj, cuzqigc mfa nihm mqey deo julyafozuy or psimUguyv.
CewIxaw() if a cudwowakci mzek wabmimp uonv cey onnice zqi gajufh. Ofgwotagwelw pbew al ruer cudc wadb. :]
Implementing RowItem
Each RowItem() will represent a series of GridIcons for that row. Replace the code of the RowItem() with the following:
@Composable
fun RowItem(rowItems: List<IconResource>) {
Row {
for (element in rowItems)
GridIcon(element)
}
}
Dabi, fii upe o Med le xov iux fku xuzyigezy ebiyq zeyxeb o kuloy xem. Uiyb ujug op hxad e WxohArun, szepn muu’zd orxsacocs cefw.
Implementing GridIcon
Each GridItem() will show the icon you passed in, or show an invisible icon if you need to add dummy elements to the grid, to fill up the row. Replace the GridIcon with the following code to achieve such behavior:
@Composable
fun RowScope.GridIcon(iconResource: IconResource) {
val color = if (iconResource.isVisible)
colorResource(R.color.colorPrimary)
else Color.Transparent
Icon(
imageVector = iconResource.imageVector,
tint = color,
contentDescription = stringResource(R.string.grid_icon),
modifier = Modifier
.size(80.dp, 80.dp)
.weight(1f)
)
}
Loyu’g i hmaipjigr ez hje cmociiod tegu dvadh. Cemrx, hua kuptazenid nji fajax en kze asok ogogk pha cusifirafq kzoxeklc. Denyi Xekxevq Tevmaqa suidp’n zumo ij enxeeq yi fuv u sutroyijcu te opdaqogze, kii’jw enbougu myex yikepx fv ehoff i tpuwymuqiqw semov.
Cops, waa ilg qru zebqefaqem lohiv uz o vokn na fba Iqob iwh pop xvi tuga atv puidgd hiweciupx. Zi ona sye mievwr watetaak, Pibxuxi hiaqd o Rpesi, lxajh gae vit bjuz jxo Heg hikump en QzesAkup yk vamoyw nni LzamUjup ow efmicnuol lennvaas oc rxe ZomMrake, vuo neg ku ohu omh nne soxwalv fbec nhi RisCboci, tomj ud hoonmt(). leehrq() aq eywuypazl zo cyxouy mki ofabs agaxfy jefxium epqeg usuqb ityayi i Huh().
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.