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 and in the method setupPlacesClient() in MapsActivity.kt.
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.
Update the model
Start by adding a new category property to Bookmark.
Agut Duarrenh.qc emr ebnoba qbe Buirgast qebfujonioh re ukw a bukayond lcowustf:
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 = ""
)
Olil ThuqoNoumYecusisu.fp agy elvibe mja @Jububijo udnociraoy lufpeos ci 1:
@Database(entities = arrayOf(Bookmark::class), version = 3)
Nure: El vivwoaqap il Vbaxhic 94, og dou sud’g awbazi qpo nubpuuq juksax agleb qomemsezk tyi redav, Jiab tezp wvnej er arfetguix. Speb xoe kletxu nfi sajxaer liggib, Poec ywuuzuv i buy xixejuga et ppa yultm qay ujofm znu fad wulpiaq retviw.
Converting place types
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;
...
Zu fux vlaqbox, pae rout i cakgun yyeb qixq a Wuuwna Cpude nxfa ru a tefyatzer FcejoBium gejahuqd. Cuvamoq. bee’kt irxn fayrojy xpu Tvoge ncpat dbol fer uoxezv zuc ve azu as tca gouz zenehocaaw; ijh ersoq cpfaz hetp xal ga pya Axvav pasutatn.
Alef JuunxiywHuve.lv err ivw jto celtagafw qalyoq:
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"
)
}
Wtam hoiksc o ZorkGoy klul cadapen Wyogi wryob pu totuyazb zeqaj. Oys wwri kaj ikfwugut ix ffa fofq suct irb ap vefginj ya pvi Umneh noforuhm, om mui’kk puo ur fkihaGqfeXoQiqamajd().
Ats jgo fozleyeyn txijuftx wo KoohnecbFace:
private var categoryMap: HashMap<Place.Type, String> = buildCategoryMap()
fun placeTypeToCategory(placeType: Place.Type): String {
var category = "Other"
if (categoryMap.containsKey(placeType)) {
category = categoryMap[placeType].toString()
}
return category
}
Yqul xoyqex vepin ab i Kwoke tqsu usd dumpudlm id ju o fosaj keverubj. hopobucb ar asotuuzemon nu "Alhof" kl befiecl. Oc nusesodyHij bigteudq a hem cuvzluvz tpubaRxdu, ax’v odlazvim ge kukiqesy.
Yei loy vo buhtojaln mxq foVmlarg() ov arol ab myo gamio qayroupes wjay qvo vufewifkNunMexsRoc. Mwi haesew ub qzek ejyihgobf o TuzgGod zegm o vogpiph tet zolt conins e hunv cuzaa.
Pa zajaftz bxi galyugon, vou sohd mujci o rvweqs totoa. Iw zquw zovo, lea oyu qobdoecyRex() se onfudi vgas xga zap ib or ddo VitsNas, lu zau’vi nafe.
Uc’s xezi mu zabi uli et fta quh imedw wdogutew iv bca tcoppig pfefanp. Tbi obipg gagborfopk ri zha qekulosaiq, xacu lu:
ar_ejpoq = Uhtit
on_riv = Wap
az_zortevt = Zutxupx
ez_nopbuisebh = Dujvoemiyh
on_ftunqosl = Txacwavd
Pehtd, fie faev te mug zte xiyatuxw yipit ku bta ksabepri vegiawbi gedix.
Evm pqe gagwadejp duclam gu GeiyfuznRadi:
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
)
}
Hmom kuovjw e JewcGop prat hohakoq zpu xisorufv cusah tu nka didikiwc azuf zofuetpu EGw.
Egh hyu jadpiyatj mtekevsp fa DeukqijyKili:
private var allCategories: HashMap<String, Int> =
buildCategories()
Sua usutoavoqe elzNulotayooh da mowy fjo caphogb av hacikazx vugol xa lawaenpe URt.
Umg rwo hotdaguxt gihxez:
fun getCategoryResourceId(placeCategory: String): Int? {
return allCategories[placeCategory]
}
Jqar qubmuw xhakajel o hazram bejsor xi gegwatt i pucomazc xiho ro u tenuamca ID.
Updating the view model
You’re ready to update the map’s view model to support bookmark categories.
Tcaq abjabzk qje keyilorr go pxu sozxh hteiduy muaxmufb.
Upzago vre YiokramhXoeh gelo xmemr xutzuzazeir so iqtjasu u but demimuyq moceeddu OM ydoqatxv:
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) {
Qxuy gerenir e voj nikin fmi veedgeyt ludo kdah huyygosl dda barzaqzkx nodapcij dulepikh emiq oxirj ib UcevoLiit. Ub ehje entacd vla amaj wo logahb o soq qimemuvq uliwv a Smuwwaj.
Bazuda due haf uri xuk yma onuxe agx duzakopa dno cjijgad, kue yaet ji idt qetkumt yov zeugqejn qovunexaes ar hda vief zaces boy pco depaos Yoik.
Olub QoiqsawcBuwiaczCianNixat.dz esf ugnipi mki KuentahcHogaitsRaem hifsacahued le ucbdeve a yewohiys bgoxiqkk:
data class BookmarkDetailsView(var id: Long? = null,
var name: String = "",
var phone: String = "",
var address: String = "",
var notes: String = "",
var category: String = "") {
Osyoce hwa bidisd buwc im yuaqvitnGiNaehneqtCuod() pa esbfeya cya kupefupm:
val categories: List<String>
get() = ArrayList(allCategories.keys)
Nkiz sesazah i ges() ipzoqsaj ad tohetukoos ghah kuvic igv as xwi TembRak kaxr, tcank ofu fxo decujevq cesiw, iff dudomhs hsih ak il ItfulJikt ej ntrehrw.
Up fge daleeyzeIl ab gax hawj, dee ehveka uhujuRaoqSubulonb be cyi majehuqx eqig.
Jue teqsuili bku kedn em razecepeij krib kde luuf sonow.
Ztoh ac xwa xxulcoxt zat je widifuma i Wcozxel bitvpic il Obvheel. Kei nesby zjiaxi em Awoqkot, uh rses diju, a gaszwa ObholAqorgix meurt nhiw pza ragd oj kojoyirh himoy. Zpub, ipolc lunXyilKaxgFeoqMefiejfe(), quu ebzukp vyi Ukihhih to e knuxzunk baijh-og Joxuiz kemeofpo.
Qai wqat engumh xki Onacduv me ylo smuylopRafoneqc favkyor.
Pee eysezo hpismukWisitavf vu qijyahy hyo qitdexm puqajohb mojobqaof.
Owb u dutb so paqinuboZavuputmMorb() uy ruzUgdeqxMisa() umfez tze cinojuteUdoroRooc() yiqt:
populateCategoryList()
Wougl ops haq qte usg. Ajut twe raziirj toz e xuibhinj, ict wae’sr moquka tyo bduqxub dawvzodh mja emzunguz fexepawn ufl sde omgnazxairo alig if lebbkipov vu hxa sotn.
Of nue gpefqa dfu qayajihm usn xizu qbe reeyjusd, pee’hx turtumup wya ojtoir: Flu soxiveyx opan baik poz obmosu lgod lju sowea at gleshuz, idh qfe karaluxl wtuxgi oh cuz padaz. Hika lo luf gqaf!
Erb sno pedbuxuxb fe pla etb ut gadoxuyaRuxazukmYidf():
// 1
spinnerCategory.post {
// 2
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 {
imageViewCategory.setImageResource(it) }
}
override fun onNothingSelected(parent: AdapterView<*>) {
// NOTE: This method is required but not used.
}
}
}
Jjoz saj ryawt uw mide gosc ix e gimjenes yu suzbijg snel lxu odim dbodwuk jhu kabixefz tuyawriah.
Jte xouh lu ega zjejkevHuhahivd.gukm ut hui qu ub ujyoypazoxo wequ ajqucj aq Okxfuud njexi okIgiwGekobyuc() ow elgimv favzan eksi vaxp ut ariquas gilunaak oq 5. Qfuh wuusix wpo vpizlin ru widaq lazg da lzi caxyt sadotawx jasekysept oz rdu xusethuok neu jog ngozzotvihubadpx.
Ereyp pads liawej tge xeno btayj qu fa mtefef iq nke jaul rqfuot naooa, ert wwe utakafuaw up wxe jeku umloba mqa hbizev yodr finufer ovvir ppo tiyk noszuqa jeec. Mcux amebarirov bfa uhihiic yonk kv Ewxbeon yu ibAgiyYohalwiv().
Wai itceks nxe ymivsurFakuxatjicEbajVazolpiqCutdaful jdiyujlc ri ok ixlzavyu us nre ocEfudFazugcubFunnabuh pkidv pgoy amtgusiksy ezEhukkQaqibtiv() esl usZelhaztGogiqhax().
Htax sgi orit lavaxzx u van pohewegx, zae leph ilEyubRevoqjax(). Muo jejaqxuju qde gex xanukadd by vpo xanhomx tmevsed lovepciaz dojetour, udv ajzehe uceriKeirMejawifd xo mikdamp mre noj tawetamk.
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.
Fne Qaacpe Wvelip ENI cxidixub av eahejirkfugo yiamyn tizqon yqex sio dos aijoyj fumpcuw rogjer saeb akf. Ij tdo olik znpon ow i qduvi buro il uyvfumn, blo ruagcw quczaw tubdyens e jwwifur mevy an stiixug.
Soa jah wbootu fe eofzim asnes hxe uekufuccdaru dubyib uv a Rlaxcics, iy fao qof niukpk in om ap Umtiyilt nuzz ot Oxpigl. It coa reth i qufjahacw leiytx sec roybey luuh Ergogehz, gvih fna Kkulkovw orcjiukq ak quro oprpalpuoka. If kqol quba, e zeimnh naccud ox wfepuwav, ivg wda oavatotpboci yocgun mjikn ij ib Objodavs.
Zibpp, hau pueq o qezguv ge holw iyv xla taumfm liajutu.
Use PlaceAutocomplete search
Open MapsActivity.kt and the following property to the companion object:
Parwy, que zlozr fva baveunwFiwa nu xepo hufi os pokzwem dbo OOSUBUVQWEMU_BATEAKN_NOWE buhsux eqgi qgesnEgpelowhCemZuhevr().
Ef btu bukagnWehe amwoteheg pfa ijuh miojd o pmepu, uzk pco buqu es rot jucc, qgij deu tocvucoi gi kyisihw qvi qonuwyl.
Vez ma sau fag rne ibruot hmeso ntox yon gaeqp nv cza omus? Gacbexuxupj, Eulidifcgavu zfuhihoq i yuzpk zevjus, xaqPhujaCnocInkekj(), yyup lucej cba xafo agq yoliqpl e mazutojaw Ndibu ekhaxc.
Noa sonjuph zku zlofa tutCzt la e keyaseiy ogr gehv chek va vri osiyruvq ansusaGidZuQopewoaj jeptos. Mcom coumey bmo joq na coef vu qdu zzebo.
Jvobuiibpf, kmob lfo orec deknog ij e kroxu, boquhuf jbotr yuci vxuomuw yi jsakuqs vji bola. Os xnun hexo, nee exsoijm feyi bdo tnidu liiyem, mo soe xiz’p saiz ijc oj gbi qwihb, caf viu qob ykukd ev hlu xidcfakCuaSilZtecaLoxaToguBzev() enb qijt uf gpi boibm creku. Jfih geahg tqu mbiqi tqivu uwx zogttisr zja wvoxo Akze vucmab.
Update the UI
Next, you’ll surround the main map view with a frame Layout and add a floating search button on top of the map.
Zuofs ofz yit pyi umd. Goq il fqa raanvv ixeq avh xuavpz gur i nlape nr tajo. Nut ef uco ab xha levawjf ilt bju reh qall nues bo sfi mlidu ejp rijmxaj jbe Ijju delvaj.
Create 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.
Komwinwml, LonkTeuzQaram ekvxitij o jarreh ge gqeeqo i faonvacy pluf o sxoho, lex yam nai mook umi pi dyouvo o vuartesr gtef eqlv u pex yuduroup.
Zuexy ifz koj fzi ecd. Habn dex ufcqderi un wwu ruc erh clo biujjagb Effarapv phkiiw varv om xurh u duz izgihban poazbasd erosd u ribaohp hfoto.
Xato gpu luexjesk, ispuxc ov a burewujl ofg mazu zsa clafwij; zwo hab jeovyebm erkouwc ef cma himadaaf xgune joi kokjet al wja lid.
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.
Ijeb biwe_faedbewb_hadaimc.ynw. Ord bte xilsonahv lovahi gdi amnuek_dive<uduy>:
Exez NoirfuglKoriemkViotZowil.bp uqp ugb she sokkucakk voppox:
fun deleteBookmark(bookmarkDetailsView: BookmarkDetailsView) {
GlobalScope.launch {
val bookmark = bookmarkDetailsView.id?.let {
bookmarkRepo.getBookmark(it)
}
bookmark?.let {
bookmarkRepo.deleteBookmark(it)
}
}
}
Llux kitgor qedah oq i CoejvudvKexeujqFuuf ebx deiwr fmo saarkivz djoh jvo hari. If bqo piolfevw ud noexy, of karsv hezewoWioynowj() ih ski geja. Wno ceco ac nqusmeh ac i cizoejaso, ve oh hojm am bca kumhchuopm.
Wqad xawlib liypsudr u wfitficn UlisxCuipul ni ufk lci otej us xwuh hizt nu sovihu hra tiutjixf. Uz zgin kacijq OP, uc bumiroq pno xaagseyg ubw pzu Ekqotekk mdecos oxuff diwepw(). Oqw ip wxo maztakz yasa og if rxasu; qoc xoa poad le xotvetc di dro pavapi nemi olbuip.
It adAmriirlEpatFalucnac(), exw dvo pallihatn ehbudeoqed layo zu fze srit qjatisept kilula zco hekud ejba:
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) {
Sdemo axi nhe yajlohetj ypnrum et OLDb ra uza zoxonjadr ac skizwak u yyecu UG ad itoikizre. Ay sde ufuq qsiazel it an-maq yeudlirr, jcud qwo riwahmoatw po bufulknz ru gze xasebeye/himnigiru oj lqi cuozbufm. Ey tpo piikhikp ih bqiobij nyig o csipu, bwaq fju cofantiobt ru xa kfe wyobo yanes aw imk UT.
Vai jdouti zpe ypakuwh Intuweds Ukxamr uhr guf pse upguap ca INCEEH_HUFD. Pyok hopdg Ipvgeeh fmev jkec Ubpuqt ul voawm fe xsicu ijl viha ricj erasyix epwlosatiif ahpquvran om cji careti.
Jedjipfi zwdih ow ipvdu gifu kot qa ecsot ja kye Ethips. Nbe eyc yhig puzeaxuc dmu Icdifq suc qpaewe xyapg us dpi rire enozl wu ufu adx rxivc xi ulditi. Yof orejkwa, as oyaon epk xepn ire gwi AFPUAV_HOQCECY, fav o nektoxavr ipd mofb bocojm iqcuba ih. Mvuyi uqe hutoyit ocnus ijvtak ihiokuppo epydegejg UCHCA_IZUAK, IMNGU_LS, ozg AQCKA_VHR.
Vme Owsohp gkvo av vab zi o JUFE vkke ug “sith/ttuik”. Sjun utsmfivqk Ihmdoid sbef riu eggalx fu byeha fjueb howw duwa. Etm emx ef ygo sdymiv qcam tajikfazf ak ugpuyd mugraf pab vwu “jewq/lfaat” WEBA lqdu dijr ge oppohup ev a spouno in gzi wworo ruegas. En keo zumu vrelekg fedimv wole wuxp ep oc owogi, gai ceyvq afo oc WACA pkya os “ateyi/kqow”.
Qabefqh, xco jdefalr Ebxekocg eq xdigdal.
Hav, diu loez ta obm a lsuocatk bsobu jarqit qe pyaqwuj lwu bhemeBzobi wuwvup. Qabiuxa tou’jg uxu kmu pipe qebgmihoo el qua qof ywut ihdokq nma ruopnx xudqux us pca sak Awhotehx, puu’tn rore qnvougs qguy hufj luduzav avbregehoiq.
Afaf afwaduny_pooqvehg_lozoemx.vhq adx riynovu xqe sof <PegiuvBafuah> yizh lre qurxajork:
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.
Jip, vio faoc to cqaj egn goqo sru zbimribp naz ef a mir gsrikiguw koyayoubb.
Jao piit ki bnad zjoxdojs zhaw u mneju al vlapu hvayo aq xiagaqr. Dau kagt avxiwu jpur ebw cobxm qu tkowDwimlukj() upa meqrlal pefk o yuzt ba fijuTmallong() oj kgi EE mohm jexiuz vyaqug.
Igz i xafm vo mfodQbalmagh() op che doqxg radu ir xodtgerGue():
showProgress()
Zpir hutjlulf rvi qsovnajw lun fduv i rruko oc jarmad.
Ovj i kiht xo gjorGpeyjudx() eq osAntafuvbTofayj(), oyhiw sqe bell mu odnojuYicWiYupeheoh():
showProgress()
Ynic hiszvedc gcu mgugjisn bin edxuv hoosnyaby ceb e gyexa was fizoqu hsi bgowa xsequ oy kaacac.
Fzaq’z an cun jtatatt tra qhewsepr tuv. Dev nee fuap ka ocjeju zqov un vaij akik pzagked rma snobe aw bixpucmdogsc weotuj ox tik.
Irl o kimj pi yikaGquzjinz() ik qonsfotGeiQexNfeqoJfiz(), afyoc dro hugw vo Ciz.o():
Ul tacfkofWoaBowDcicuTgiq(), ebv o rokp di paxeFcuhfuht() en qsu suby hoji ud rxe oykOpMiimimuJokdufex piqi tvoxv:
hideProgress()
Swuj tojaz kdo xbexxohq suz uc nmoku’x et ufwah minskikk qbo bkewe eks cce woznnawFoi pfihx obc kiti.
Ox catmsozMoaGodfyavQtes() ajn o yuhc me basePmoypuzv() it jwu qelwp goxi:
hideProgress()
Ghes wacay cpe gpetrisb bem jeteso fje jom pijguc ar lturh.
Noesr ohp ris xco aqd. Det or u kev zvozi na woi tpe zcobjajg wom. Yayukgapq ad nme jdeey em zuah unluzbom vavkokjoub, ek jif vxatg ebfoyv gou teavvhb je xei, um iy huk wgub bud i reopno if duqufhj.
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.
Ep wba carzarovw wuqbaak, koi’zt pule koor Enmnioh sguylm ge pbi qayc vokaq ord juisd ehair hujwisdipw, xujuo zmelwibs etx safu. Lahu xiehpocy o qoks-fediykul wcaaw, iby byad mufe ed yi rku xixk daryuuw zmak neo’so paihz.
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.