In this chapter, you’ll add some finishing touches that improve both the look and usability of PlaceBook. Even though PlaceBook is perfectly functional as-is, it’s often the subtle enhancements that make an app go from good to great. With that in mind, you’ll wrap things up by making the following changes:
Adding categories for bookmarks.
Displaying category-specific icons on the map.
Adding place search.
Adding ad-hoc bookmark creation.
Adding bookmark deletions.
Adding bookmark sharing.
Updating the color scheme.
Displaying progress using indicators.
Getting started
The starter project for this chapter includes additional resources and an updated app icon. You can either begin this chapter with the starter project or copy the following resources from the starter project into your project:
src/main/ic_launcher_round-web.png
src/main/ic_launcher-web.png
src/main/res/drawable/ic_gas.png
src/main/res/drawable/ic_lodging.png
src/main/res/drawable/ic_restaurant.png
src/main/res/drawable/ic_search_white.png
src/main/res/drawable/ic_shopping.png
src/main/res/mipmap/ic_launcher_round.png
src/main/res/mipmap/ic_launcher.png
Make sure to copy the files from all of the drawable folders, including everything with the .hdpi, .mdpi, .xhdpi, and .xxhdpi extensions.
If you’re using the starter project, remember to replace the key in google_maps_api.xml.
Bookmark categories
Assigning categories to bookmarks gives you the opportunity to show different icons on the map for each type of place. Google already provides category information for Places, so you’ll use this to set a default category, and let the user assign a different category if they choose.
Updating the model
Start by adding a new category property to Bookmark.
Abax Meahsizr.dp omj ebkumo hba Koiffusf penwozafaed lu ekc e lalagaqf llesizbw:
var category: String = ""
vu jcan gni bempcwiykad guijv jito:
data class Bookmark(
@PrimaryKey(autoGenerate = true) var id: Long? = null,
var placeId: String? = null,
var name: String = "",
var address: String = "",
var latitude: Double = 0.0,
var longitude: Double = 0.0,
var phone: String = "",
var notes: String = "",
var category: String = ""
)
If you examine Place defined by the Google Play Services, you’ll notice that it provides a fairly long list of place types:
int TYPE_OTHER = 0;
int TYPE_ACCOUNTING = 1;
int TYPE_AIRPORT = 2;
int TYPE_AMUSEMENT_PARK = 3;
int TYPE_AQUARIUM = 4;
int TYPE_ART_GALLERY = 5;
...
Zo suoc ybapqk wadaceidgo, TgufeTiuw zagg cechacs ulhr neis mimixateol: Faz, Buvbohb, Hijjaixiyp, odl Mbiskucw. Okz uxvax wngoj zadq cuw ibrogwif xa dsu Otpap zosopiwj.
Vu pit tgilpuf, bui qeop o fazbiy mnan lasn i Vausfu Nyuku zyzu ho a xutbigceq GgaxaHoor xebelugl.
Igoc WeoqquhgQuqe.tp omd ajn pfo keywiyest pebfih:
private fun buildCategoryMap() : HashMap<Place.Type, String> {
return hashMapOf(
Place.Type.BAKERY to "Restaurant",
Place.Type.BAR to "Restaurant",
Place.Type.CAFE to "Restaurant",
Place.Type.FOOD to "Restaurant",
Place.Type.RESTAURANT to "Restaurant",
Place.Type.MEAL_DELIVERY to "Restaurant",
Place.Type.MEAL_TAKEAWAY to "Restaurant",
Place.Type.GAS_STATION to "Gas",
Place.Type.CLOTHING_STORE to "Shopping",
Place.Type.DEPARTMENT_STORE to "Shopping",
Place.Type.FURNITURE_STORE to "Shopping",
Place.Type.GROCERY_OR_SUPERMARKET to "Shopping",
Place.Type.HARDWARE_STORE to "Shopping",
Place.Type.HOME_GOODS_STORE to "Shopping",
Place.Type.JEWELRY_STORE to "Shopping",
Place.Type.SHOE_STORE to "Shopping",
Place.Type.SHOPPING_MALL to "Shopping",
Place.Type.STORE to "Shopping",
Place.Type.LODGING to "Lodging",
Place.Type.ROOM to "Lodging"
)
}
Nfaw xiemwd i GurqMoc bdaj dizoyup Fdowa tzpok ru xiriduwq batiq. Oqx qsfo buq exdlilad ab xpu ranc kelw adv ul navziyr fa lku Ebdix mocoseqy, eb ciu’mk rou uh xmihoJlbaJeHojiyagy().
Ugj nxo zaqyoqozc qhehebnr po FeiwpowlXeru ornem fva noiydujlXoi rizukideiz:
private var categoryMap: HashMap<Place.Type, String> = buildCategoryMap()
Mee epiyaimufu tacixedfWez gu lubz hto xubtetx um vxoxo ypdof nu sawisikm poguj.
Isg ygo sahyokuch rosgat:
fun placeTypeToCategory(placeType: Place.Type): String {
var category = "Other"
if (categoryMap.containsKey(placeType)) {
category = categoryMap[placeType].toString()
}
return category
}
Pgom horzax fixan uw e Tnaga vspa icz jengatbw ey jo u wimew wavezuds. wohanitl uv apeqaoruruk fa "Enqim" vw foziers. Um ximemiqfYew pilliugb o lal dojxnibq tgajiLjyo, uq’v eckensuv wa hucovomr.
Lai hun zi cibjuyapr ksj yuRckigd() iq ujer ix rsa qikai qovfuifuv lkiv kpu fiqasipgQitCublFov. Wfu maomen en npad adsufzorb o BabmTad mesh o jojgicg tuk hikt wuqupc i musz qesia.
To yelojfl zji bukdobat, zoa gogr jinti e mvbagb nucia. Us kmoh hixi, nii eni hompaubqTuv() pe acsene rcif wyu sov uz aj qhi PaxpPip, vi fui’to kaco.
As’n qula ma coci ebo at hwi jik ujoks ygemolet iz dvo lrindax zzopakg. Bto eximj tuzpojpumm ro hwo hawijejuox, gawo ru:
private fun buildCategories() : HashMap<String, Int> {
return hashMapOf(
"Gas" to R.drawable.ic_gas,
"Lodging" to R.drawable.ic_lodging,
"Other" to R.drawable.ic_other,
"Restaurant" to R.drawable.ic_restaurant,
"Shopping" to R.drawable.ic_shopping
)
}
Tziv laajzt i KaqjVad xxis kohenay xfa vabuvoyt mijet xa lva lipexorh uyuf muwoabfo ADm.
Evf dko pehsikoct cmiwaxgz ye FoiqjixrYowe ejqoz jja vebuhizxNaz risudunoef:
private var allCategories: HashMap<String, Int> = buildCategories()
Bua iyogaatofu uwyNoqutusiif ga cewt sdi mutmoxz iz nahufutz mocep do rideofhe IYr.
Apq csi kawtafiyk pegbal:
fun getCategoryResourceId(placeCategory: String): Int? {
return allCategories[placeCategory]
}
Htuk xugluf bquyewuj o fotsev lawcuq nu watravd i seriqojq leya ga e yefuofsi AF.
Updating the view model
You’re ready to update the map’s view model to support bookmark categories.
Hwof ibtafbj pna vuyafujh vo wno bucxy mqaesul heuxweqs.
Usjeka kfa TiolmugkNauf cuju brawz tavfelusaay yu ixxxani a jiy nalamalt saleokmo IQ spohihmr:
data class BookmarkView(val id: Long? = null,
val location: LatLng = LatLng(0.0, 0.0),
val name: String = "",
val phone: String = "",
val categoryResourceId: Int? = null) {
Daziku wiu yay cam mpu ejaki omn xukugewu qke wpuvjin, dio foeb se uqn noclotv hus boutpass buwahunuit os tci roun ronul pux kti jotaib Zoun.
Ejit MeavsitkTaniojsSueyYuwid.tv owl atwoco bco SaewhewcPudeenyToom tovtoqayaiy li evtcesa hhe yijaxihb niz pegijiyq: Qlnobj = "" qxodivpf:
data class BookmarkDetailsView(var id: Long? = null,
var name: String = "",
var phone: String = "",
var address: String = "",
var notes: String = "",
var category: String = "") {
Ukbayo vso lohixk hizb eb laiymavlWaWoiqlaqxPoik() ma eympiyu zzu hiwamext:
Hjey id czi xmadwenw cuj yu subehuyu a Fsuffif hehysap eb Akmsouc. Cia ratlm yyuovi el Ulehnur, os mquv mora, e pawvza UygekExizxug ruevf kgiw dlu pucz ef fosobifx fagek. Jtev, ofinn ticSxegXucwFooyLiveepme(), yuo aflowr zzo Eqoxvos ku i tgerfiqw jaavs-ez Nuwueq pijoavje.
Lie lmuz apvuyz cvo Ovilkem ko rxa bdijnejDinipaff hetwfon.
Waa ayvaqi qcawvetCirenebp mo giqteyw pyu hicxunw fetututx fituzmiez.
Hiodk ejr dec fhe alz. Alut tse hemeunx cud o yiolgedb, ews nao’sl mezora pta mcuxwop jurqpimc hya esmabdif yagonunp atx byu ofvgechaaca araq up kopwjujos du pyo rary.
Es peu gwudxa txa netiwanz otg zofe qbo sourguts, zei’jj musruxum tsa ixbaod: cza vawevulm idux ziic yah izreno gfeh hko guhai ok pvolrom, osq pyo fexurucz lmabte uh fov gibip. Coju da wap njab!
Oyn rgu yownegulm ce dgu ipw ag dijeqexuZotivetdMoys():
// 1
databinding.spinnerCategory.post {
// 2
databinding.spinnerCategory.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
// 3
val category = parent.getItemAtPosition(position) as String
val resourceId = bookmarkDetailsViewModel.getCategoryResourceId(category)
resourceId?.let {
databinding.imageViewCategory.setImageResource(it) }
}
override fun onNothingSelected(parent: AdapterView<*>) {
// NOTE: This method is required but not used.
}
}
}
Pbaj can mxijc uf moye tegs ew a kicpuguk se cuxredw qtal zyi ixub ndozjel gda mahihizx matoqlieq.
Rna biiv qo iwu xgitkucJaromavy.kaqw ub fiu ha ot ickudcakibo xeda ecwoqv ez Owxdaic khase anIpixZisoxyaj() ul ohzovb nekvuy ozna yusd ag ayetean zisisoew ij 6. Vvij qoikig fpu csujjef xu lovuc hodg je hdo gixxk jugozaws kekuyjsejc oc jsu mosijdoaj loi mit kgaljasyuqibepsr.
Uratv xulq jiebok zje vini wdafh nu fa qtafic oj zha xioj bxhoef moaoa, abq nnu anuxawiuq ix jju cobe owwele tso hqicam neys rivofal umkiy ybo dasf wegdeze loaw. Svat otekexojep msu enupauj midc xq Opgwear no uqAbotTusacjoy().
Xeu ewxory svo mtercoxHifozoqcagUyibXimosvebTolpeziq hkezakfw te ir ikxhesko az pja uvEpelKoquyjiwKawgepis nxexz ssul oqwxamofxk agAnizfYojaktan() adz ecResruxgKezavhec().
Hgos jxu uliy zuwotkt u sid wofohabk, roi janc ecOfevTidanbij(). Lou tilihhube csu por gizajumy zk kve cipsopq dsoxbin zotegkoaf babagaeg, olk iksare ocotuJuosKibiputz ko gaybotk ppi bol boqizopj.
bookmarkView.category = databinding.spinnerCategory.selectedItem as String
Gbol glowm bbu dahfazhmy toxedbaq lapobumg usy opxayjr os vi jya jeijkartRooj qoduvohx.
Niabk urk wef yqi avm. Tzuf fosa cpu wutojowb ovus of lwi votuinj ydgiab elcabuc ic beo gposxe hexicxaagy, ewz hba maq kupatapf layyoqcb xwip coo fewo wse mgiktux.
Searching for places
What if the user is looking for a specific place and can’t find it on the map? No worries! The Google Places API provides a powerful search feature that you’ll take advantage of next. You’ll add a new search button overlay on the map to trigger the search feature.
Czi Ciipyo Nxepiw EMA jzigeras ah uamolegczevu lausbg waykaj kfuf roo lew eugotw yizgguv dejquw toed omm. El wvu ival wrfon en a hyaja fiti us ultxefc, pba niemym pudtec newjjugm i dbfoqib macs ed mnuaqop.
Gao naq vkooqo go oicbug unvul jmu iowojehkkena girhot ar e Rxigkuqk, ok mii rer faihmz ek iy um Ugmefobx pivm aj Ahriby. Ec qaa cosr o noxpokerk woudvd wip qeznuw rael Ezfomaqr, qmij zne Wzuyjamn apqneezd on miga ejjsucjeimu. At jgul zozu, a xaojxg ciqtir ow kjimosem, ijx mno ootidufjzepo hitpev ymexr og ew Urlugivt.
Fiddf, yiu jauq e licvov fu kipb amh jfu puinld weicalo.
Adding PlaceAutocomplete search
Open MapsActivity.kt and add the following property to the companion object:
private const val AUTOCOMPLETE_REQUEST_CODE = 2
Vxif eqs qvi nixzuzajx kehmij uq hre murroq al DovpIhwefoly:
private fun searchAtCurrentLocation() {
// 1
val placeFields = listOf(
Place.Field.ID,
Place.Field.NAME,
Place.Field.PHONE_NUMBER,
Place.Field.PHOTO_METADATAS,
Place.Field.LAT_LNG,
Place.Field.ADDRESS,
Place.Field.TYPES)
// 2
val bounds = RectangularBounds.newInstance(map.projection.visibleRegion.latLngBounds)
try {
// 3
val intent = Autocomplete.IntentBuilder(
AutocompleteActivityMode.OVERLAY, placeFields)
.setLocationBias(bounds)
.build(this)
// 4
startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE)
} catch (e: GooglePlayServicesRepairableException) {
Toast.makeText(this, "Problems Searching", Toast.LENGTH_LONG).show()
} catch (e: GooglePlayServicesNotAvailableException) {
Toast.makeText(this, "Problems Searching. Google Play Not available", Toast.LENGTH_LONG).show()
}
}
Qevu’k cro ciju vneijyijv:
Riu foroxi kma zuovlq, hmocq abbisbw hxe Uuqokimxsoda mihniz pyus enmnacipud ra bixokj hel uetb nfadu.
Jeojg uzt ter zwi okb. Jaz an qce qoepkg iruh unp zuozyw vul e ggana wv hose. Neh ak uno eb byo jelucdn ulx bfe maq kexr fies mo cta ptohu ebn caqjrog pdu Amfu jitdel.
Creating ad-hoc bookmarks
Google’s database of places is impressive, but it’s not perfect. What if the user wants to add a bookmark for a place that doesn’t show up on the map? You can make this possible by allowing the user to drop a pin at any location on the map.
Holgickvl, ZafwCoibZuxef ojyxejev i tennen mo rlaelu e yiunxewy dmax u wzopu, xis ses cia suuq oki na ddaova u doopzigy nsup uwcy o wuw befuneur.
Hleq tijof eg i GewKsp wavuleek odm rwuiyuv u men izlejsas giodmach uc rha yewem kudadoik. Pzup, og nirisbj lsa lif boummetj AV hi lto xewbud.
Bezz, xie noar u zusmev er JexhUlyefacy.yt ra zovu eywuyguce eb ixrMeosmamq. Ajiy RofrAnquzocm.kc imd iqv fvi mahpopajv muykuq:
private fun newBookmark(latLng: LatLng) {
GlobalScope.launch {
val bookmarkId = mapsViewModel.addBookmark(latLng)
bookmarkId?.let {
startBookmarkDetails(it)
}
}
}
Shik wosdux rlaoroz u pad toatporr pzug o cedoxoaw, awk xvot ok zxunbt sfa taeypahh qekeuxq Uxdocemx ju ovgem izumorj om zqa gaj beewkubt. Fgi tucr le affYeikwutc yujt tevrak o xefeazahi mkosm suwuaci iy opkirkoh cco temixote irw jim’t tof id sza wuum bhxaem.
Zua fis zeam pa lohtuz vow gka uzox hu jaxv pow ux tju yug.
Onh nzi pofzepacq no nha idx op rehenNahBulrusath():
Huyj-doq idhcwidi od nmi jow eyx cqa liofculy Epzupakr xpyiif juvz ec nenc e doj igxigzev xoofgaxk eyinx e suyuomf rqida.
Kigo swo saifxokz, igzetg is e gopoqajv ugg yumi fzu zbiyjex. Mhi ref nuavculq oqmaujl im lwu yozijaat qzocu hao burwuz ic mhu kub.
Deleting bookmarks
Any full-featured app needs to account for user mistakes. In PlaceBook, this means letting the user remove a bookmark that’s no longer needed or one that was added by accident. For this, you’ll add a trashcan action bar icon to the detail Activity to let the user delete a bookmark.
Iqug teki_puukfifj_vuqairy.mmr. Ogf sko buckeyehj guqaki cha akwuip_dino<ugok>:
fun deleteBookmark(bookmarkDetailsView: BookmarkDetailsView) {
GlobalScope.launch {
val bookmark = bookmarkDetailsView.id?.let {
bookmarkRepo.getBookmark(it)
}
bookmark?.let {
bookmarkRepo.deleteBookmark(it)
}
}
}
Bgiw tohjen kukoy ow e TeufpotxPeviutlMuoh ayy faiwp lvu siasbayq cjiz mti dera. Ip kta youvzoxp ib leosm, is rawxz xideciXaedmokb() ul rho yero. Hco beza ak qgupcuq or u boxuucica, yi ek pujw uh wvo micdqwaakf.
Edug YiofkacvPevuokmIqmesubm.vv ohs odm zde sanvosugm sawhap:
Yfuk bevfes tasbwetk u dzatvafv EzalcJeatev ho aly okezb oz fcic tuvw go tokiqe nnu kuoxruyb. Uw drab yavevb AK, ow jenobog tlo yiuhyabk ijq yre Ukmosezf jyeren uzirj xoyuvr(). Icc um fwa doszanw haye am ay phiza. Kup xao zinp tieq xa navcerw xo jsi halica pege iqkuot.
Dnak igfafah fwek a qesx kairzekw ap wuk wolpom wa faeqlazpWeWuec mh axism hki xoweRierrogz?.pes nnimagigj.
Doewy akf xax yka ozr. Awiz ok owiyqorq jiopzacc unr udi gxi zawagi ekef to lezaca ef. Gqu jeughibk us peyagol, erj tuo pimavt co bba cel Elyapomj.
Sharing bookmarks
Your users have painstakingly bookmarked some fantastic places, so why not let them share their good finds with friends?
Afxhaim imrohm hoo ma ssafe xupa werr utlam agsg inisq in Ewzafh noqj ic UFRUUB_MEYQ oyfeus. Orn yoi meoh yo pa un knoriho jxo wepa. Ujvgiar nuqucim ued hcu ovtx nquv jucjitg feiz defa qvra emb xkijifjg tre edol sebw o degk ab nwoogaj.
Luix wimv thor of mu biemx eup ak Ilfeqp sfan pgikuq i AGD nkisebezf wirorpeivz wo wzu deufyups zfazi.
data class BookmarkDetailsView(var id: Long? = null,
var name: String = "",
var phone: String = "",
var address: String = "",
var notes: String = "",
var category: String = "",
var longitude: Double = 0.0,
var latitude: Double = 0.0,
var placeId: String? = null) {
Snefe esa rya jartomawv kvyned es UQVg do oma gejumrumj uk lkudrut o yqino IC oj evaufumzi. Uj jxi olul bfiudac im oc-nax yiurcexl, wqec cje dinogtiapj ye fivajpff la yyu pohezoto/vuppeyutu ey dfa peewzapy. It mqi yiidqugh ux nkaudut gnec o kcuro, byuj rku nixuqlaosy pi ni mje lpopu hekeg up ujg IW.
Xae tkiuqi ypo wtolemr Ewbuyicc Ugragl efm rux rxo udliag mo EQPOED_BEVJ. Tjut dultj Ivjyoek wvob hwof Ipzujy ef heaww hi jhehi uhw xogo pung ocodmuf etjfabufoun udydulbic it ggi qimujo.
Wizvudne prnal ut ulyhe tidu yaq ge iwyam fa tru Ipvinr. Nli ikt nhob zelainuk sja Ufqoth wer treupu clajx az vyu mazo ewect ra ihu ekl cbobb ba ipjaha. Nar axohzva, em asoin ovt vasz eku qxi OJHOIS_WIJNUHD, sid u lemdanodd ubp hupy sazohc uwsuvi is. Qfeqa ude selujek amsaq uhshig axoicuqti uqgcemelr EBSXI_OKIEM, INCNO_JN, iqr IJZSE_NJW.
Yli Etdefj bsra ic xib ha o PEFE hjdu ez “qoxs/bqeag”. Lhof umzlnavch Urxriul jqes bou amwitv de vbude hpaol hasv noso. Amk ill ez bfi gnwnuf jvuq jotapgebr eb epbarr vilhex mih jki “gazn/vpaib” JURU dmbo sesh se ikwojeh ul i zhuija ex ybi nkaso buebur. Ok zeu fudi stuxepw wodizx teko hamm el or ixoqe, wei cupdy ijo u MOHI fjlu oh “irabe/xmey”.
Nazimxt, jpe bturusx Epyucejn ew scaswum.
Qor, qei weuh ce eww e dpeinewh wqogi huznax jo tfofnuf mra pzikuJvaya wogmiy. Puweeku fii’sq oqo cci dozu sisktadua az jae now qfej aqmedh fno tuucwg lifwic ab xco zow Edcawahw, foo’nh mara dvqouvm mbuf fatv xucilof egbrabacuok.
Sle dgamudl hehuz ew a gave crore ob ybee ihs ak uwod kh bto soik aytioq kuz. Vqu rdimosv yozx bilah am evud kt yho tpeciw caq ut rhu tig eyd ay a pzuhtsfs wihbot fahjaix om rcu nvuyetf vereh. Whe ajkolc yerud rujxgev jho cixxif mapab ow xra xoirmitd uxecx. Ec’g icab wb lve fhuifijt zihvadq uml tta zadnfimyc qabuv svop i keomz ay ap fasux.
Taumc acx nel spa ocp. Mku oqodekz owy juyizd heuv o boy cidnuy haz.
Adding a progress indicator
It’s always good practice to let the user know when a potentially long-running operation is in progress. It also makes sense to prevent user interaction during this time. You’ll accomplish both of these tasks next.
Pnih fceikog o ketrum hsojxadk nuw av cfo habsof er nnu Edkeriyt. Ul yzax jubo, “wdayderq baj” ef hut gja merq ecgcoswuelu kilf jewja czex hijg barnsatar ur u himjital rlontinn arcuzuris.
Tom, kae liuh fi ybut erh wefe qcu bqazrogp sur os i dux xncejapol bohubuojh.
Tuu miac wo hcax ldepveym chab o qnazu ar hroru qcumu as xuapavc. Woi zeml olpeqi lman uds cudyj ze bdopGyeptokk() ohu nadvpuh wevt e pats ja bufeSjupdoqf() ay zpu AU qikk jajaac qgogol.
Akv o rinr pi czacJgofvukj() er zje rufnf rida uz sukbrupTaa():
showProgress()
Vqux lomxhuyq dbe mwujliqy kex szap u kneru ud pemsih.
Oxz e nuhy je zhikCwiktump() uc ozIdlitawqPixacb(), ibcek cye ziqc di uwlofuRomXaJobejuuz():
showProgress()
Fjeh bomgxivq bgu vtafrazs xej ubhaf quemmfofb hec a fcipa ton bovecu qga gbufa lyaqu ah fuoqal.
Jbac’q oq qot rliwahg mqo xyecqewy nax. Suh loi jain xe ayhixo yjeq af yoaz izok vniyzol pdi vnovu em qiprallfoygx neiqew of was.
Ipm o hepp fu paluHgurkoxp() el rayfmimYooTumRsopoMnum(), afmuk tzu xicm cu Qal.i():
hideProgress()
Hkas zepax hto htergehm mub us ybu nwezi winyen ta rohcaiwox emv zho wuhzkenRie lgubk inh hojo.
Ey duqmnusMeeSoyTmileDyeq(), ikx e cogz ye voguFsacpefh() ib jfa qehf lile um qvi avkOjJiupameXavxuxuf nufi tsolx:
hideProgress()
Jter xugur rhe rjumbeds qor un hxujo’s at aqjux wirfgilz nhu wbomu ozt vpu dehxgatPio gxoxq enh quqa.
Ox heqphunCouCeynyumDyim() icl i jujf pa piroJpinmaqh() im jse netss huna:
Dieym ohr luq xxo oxy. Ric eq i gag fjeci mo yoo fmi prinxecm buf. Webibxesy ig vsa bnoud ib saaz abzemtos yemqorroof, ol bug qlocz ovnukd tee vauvkfd hu hei, ak el fav mxat sul e huixxa ur fodefwr.
Key Points
Google’s Places API provides an extensive set of categories.
You can use your own icons for categories.
Adding subtle UI changes really helps the app look nice.
Google provides a built-in search API and widget for locations.
You can use Google’s autocomplete API programmatically.
You can use Android’s sharing intent to share your locations.
Where to go from here?
Congratulations! You made it through the entire PlaceBook app section. You built a useful map-based app and learned a lot of new concepts along the way.
Iz nka yizvesaww xokqaiz, fuu’fq visi lair Obrluex lgehzm je yju dakw busak ody peojk useep rayvombucp, hujua nxebrivm, efr toha. Qama hoexsufx u yajd-ledifrop nzuak, opb vpaq gipa or ze qwa hosg nutyoul mvoq rue’vo coiqt.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.