Cu peste o treime din oameni care opteaza pentru a crea relatii semnificative online, este potrivit doar ca aplicatiile de intalnire instantanee, precum Tinder si Bumble, sa fi inflorit. Asta m-a gandit – cat de greu este sa creezi de la zero o aplicatie de datare mobila care constientizeaza geo? Se pare ca, cu microservicii si modele de design fara server, sustinute de o retea in timp real, nu este atat de greu.

In acest tutorial, vom acoperi doua parti foarte importante ale construirii unei aplicatii de intalnire pentru mobil, geo-constientizate – geolocalizarea si transferul.

Striga-te lui Dan pentru ca a facut asta!

Microservices Architecture pentru o aplicatie de intalnire

Sa acoperim fluxul aplicatiei noastre si sa acoperim o privire de ansamblu rapida asupra a ceea ce vom construi. Pentru a mentine lucrurile simple, cand spun utilizator ma refer la persoana care deschide aplicatia Android si cand spun partener (e) ma refer la orice alt utilizator care deschide aplicatia.

Stim ca trebuie sa gasim fiecare partener in afara de utilizator si, de asemenea, trebuie sa stim locatia acestora. Aceasta inseamna ca fiecare dispozitiv trebuie sa partajeze un ID unic si locatia lor. In continuare, avem nevoie de fiecare dispozitiv pentru a putea verifica unul impotriva celuilalt dispozitiv, adaugandu-se, de asemenea, la lista sau actualizarea locatiei lor curente. Odata ce utilizatorul s-a adaugat la lista partenerilor, putem alege pe ceilalti utilizatori din lista si verifica distanta lor fata de utilizatorul curent.

Asta inseamna ca putem imparti intregul nostru sistem in trei parti:

Aplicatie Android

Aplicatia Android reala care trimite propriul ID unic cu locatie si primeste ID-ul si locatia altor utilizatori.

Salvati si filtrati

Aceasta sectiune ingereaza date din aplicatia Android si returneaza locatia si codul unic al fiecarui utilizator care nu este cel care a apelat serviciul.

Calculati distanta

Aceasta implica un utilizator cu locatia sa, precum si locatia unui alt utilizator si scuipa distanta. Exista unele aspecte matematice, deoarece vom calcula distanta dintre doua distante de latitudine si longitudine. Acest serviciu va returna utilizatorul unic si distanta.

Crearea microservicii

Pentru a face lucrurile simple si eficiente, trebuie sa gasim un furnizor care sa functioneze microserviciile noastre. Pentru a face acest lucru, vom folosi functiile PubNub.

Mai intai va trebui sa va inscrieti pentru un cont utilizand formularul incorporat de mai jos. Dupa aceea, indreptati-va la Panoul de bord de administrare si activati functia Functii.

Acest lucru ne va permite sa construim functia Salvare si Filtru  , precum si microserviciul Calcula distanta pe PubNub si ne va oferi experienta scalabila in timp real pe care o dorim.

Salvarea si filtrarea utilizatorilor in timp real

Aplicatia noastra de client va publica ID-ul si locatia utilizatorului curent intr-o functie PubNub fara server, care va salva locatia intr-un spatiu de stocare persistent pe intreaga cheie numit Magazin PubNub KV.

De acolo, prima noastra functie va verifica codul curent cu fiecare articol din magazinul KV si il va adauga la lista de utilizatori. Dupa ce vom avea lista completa, vom publica acest mesaj inapoi la canalul care este unic pentru dispozitiv utilizand ID-ul sau.  

export default (request) => {const kvstore = require (‘kvstore’); const xhr = necesita (‘xhr’); const pubnub = a necesita (‘pubnub’); const {location, id} = JSON.parse (request.message); oameni var = []; kvstore.set (id, {lat: location.lat, lung: location.long}); kvstore.getKeys (). apoi ((taste) => {pentru (var i = 0; i <taste.lungime; i ++) {if (taste [i]! = id) {people.push (taste [i]) ;}} pubnub.publish ({“mesaj”: oameni, “canal”: id}). apoi ();}); cerere de returnare.ok (); }

Nota: Functiile PubNub permit maximum 3 solicitari pentru fiecare apel de functie.

Calcularea distantei in timp real

Vom primi datele sub forma unui tablou. Primele doua elemente ale tabloului sunt ID-urile utilizatorului, iar ultimele doua elemente sunt locatia utilizatorului care a initiat solicitarea. Primul element este ID-ul initiatorului, iar al doilea este un posibil candidat. Dupa ce terminam calculul, vom trimite ID-ul utilizatorului unic si distanta de la care se afla initiatorul.

export default (request) => {const kvstore = require (‘kvstore’); const xhr = necesita (‘xhr’); const pubnub = a necesita (‘pubnub’); const message = JSON.parse (request.message); console.log (mesaj); kvstore.getItem (mesaj [1]). apoi ((valoare) => {var location = JSON.parse (valoare); var distanceDelta = distanta (mesaj [2], mesaj [3], location.lat, location.long , “K”); pubnub.publish ({“mesaj”: {“ID”: mesaj [1], “distanta”: distantaDelta}, “canal”: `$ {mesaj [0]} – distanta`}). then ();}) return request.ok (); // Returnati o promisiune cand ati terminat} distanta functiei (lat1, lon1, lat2, lon2, unitate) {if ((lat1 == lat2) && (lon1 == lon2)) {return 0; } else {console.log (lat1, lon1, lat2, lon2); var radlat1 = Matematica. PI * lat1 / 180; var radlat2 = Math.PI * lat2 / 180; var theta = lon1-lon2; var radtheta = Math.PI * theta / 180; var dist = Math.sin (radlat1) * Math.sin (radlat2) + Math.cos (radlat1) * Math.cos (radlat2) * Math.cos (radtheta); if (dist> 1) {dist = 1; } dist = Math.acos (dist); dist = dist * 180 / Math.PI; dist = dist * 60 * 1.1515; if (unit == “K”) {dist = dist * 1.609344} if (unit == “N”) {dist = dist * 0,8684} return dist; }}

Rezultatul acestei functii va arata astfel:

{“ID”: “ID utilizator unic”, “distanta”: 5}

Cum sa treceti prin utilizatori in aplicatia Android

Pentru a incepe, creati un proiect Android Studio gol cu ​​suportul Kotlin bifat.

In continuare, uita-te la dependentele pe care le vom adauga la fisierul nostru la nivel de aplicatie Gradle pentru a ne asigura ca aplicatia noastra functioneaza fara probleme.

dependences {grup de implementare: ‘com.pubnub’, nume: ‘pubnub-gson’, versiune: ‘4.20.0’ fisier de implementareTree (dir: ‘libs’, include: [‘* .jar’]) implementare “org.jetbrains .kotlin: kotlin-stdlib-jdk7: $ kotlin_version “implementare” com.android.support.constraint: constraint-layout: 1.1.3 “testImplementation” junit: junit: 4.12 “androidTestImplementation” com.android.support.test: runner: 1.0.2 ‘androidTestImplementation’ com.android.support.test.espresso: espresso-core: 3.0.2 ‘implementare’ com.google.android.gms: play-services: 11.6.0 ‘implementare’ com.github.bumptech. glide: glide: 4.8.0 ‘annotationProcessor’ com.github.bumptech.glide: compilator: 4.8.0 ‘// Implementarea bibliotecii de asistenta’ com.android.support:appcompat-v7:28.0.0 ‘implementare ‘com.android.support:recyclerview-v7:28.0.0’ implementare ‘com.android.support:cardview-v7:28.0.0’ implementare ‘com.android.support:design:28.0.0’ // Vezi implementare ‘com.makeramen: roundedimageview: 2.3.0’ // Implementarea Vizualizarii cardului “com.yuyakaido.android:card-stack-view:2.2.1”}

Prima dependenta este SDK PubNub, care ne va ajuta sa publicam si sa ne abonam la logica pe care tocmai am creat-o. Legat de SDN-ul PubNub, vom avea nevoie si de cheile noastre de publicare si abonare. Puteti obtine cheile de publicare si abonament parcurgand configuratia rapida de mai jos.

Celelalte dependente necesare sunt pentru componenta vizuala a aplicatiei noastre – functionalitatea swiping.

Crearea interfetei cu utilizatorul

In primul rand, ne vom ajusta activitatea_main.xml pentru a se adapta functiei noastre de swiping care va fi initializata in fisierul nostru MainActivity.kt.

<android.support.v4.widget.DrawerLayout xmlns: android = “http://schemas.android.com/apk/res/android” xmlns: app = “http://schemas.android.com/apk/res- android “android: id =” @ + id / drawer_layout “android: layout_width =” match_parent “android: layout_height =” match_parent “> <LinearLayout android: orientation =” vertical “android: layout_width =” match_parent “android: layout_height =” match_parent “> <android.support.design.widget.AppBarLayout android: layout_width = android” match_parent “: layout_height =” wrap_content “> <android.support.v7.widget.Toolbar android: id =” @ + id / toolbar “android: id =” @ + id / toolbar ” layout_width = android “match_parent”: layout_height = “wrap_content”> </android.support.v7.widget.Toolbar> </ android.support.design.widget.AppBarLayout> <Android RelativeLayout: layout_width = android “match_parent”: layout_height = “match_parent” android: clipChildren = “false”> <LinearLayout android: id = “@ + id / button_container” android: orientation = ” android “orizontal: layout_width = android” match_parent “: layout_height =” 80dp “android: layout_alignParentBottom = android” adevarat “: android paddingBottom =” 12dp “android: clipChildren =” fals “android: clipToPadding =” false “> <RelativLayout android: orientare = android “orizontal”: layout_width = “0dp” android: layout_height = android “match_parent”: layout_weight = “2” android: paddingRight = “16dp” android: paddingEnd = “16dp” android:clipToPadding = “false”> <android.support.design.widget.FloatingActionButton android: id = “@ + id / skip_button” android: layout_width = “wrap_content” android: layout_height = “wrap_content” android: layout_centerVertical = “adevarat” android: layout_alignParentRight = android “adevarat”: layout_alignParentEnd = android “adevarat”: hapticFeedbackEnabled = android “adevarat”: src = “@ drawable / skip_red_24dp” aplicatie: backgroundTint = “@ android: color / white” app: fabSize = “auto” app: rippleColor = “# 22ED7563” /> </RelativeLayout> <RelativeLayout android: orientation = “horizontal” android: layout_width = “0dp” android: layout_height = “match_parent” android: layout_weight = “1”> <android.support.design. widget.Android FloatingActionButton: id = “@ + id / rewind_button” android: layout_width = “wrap_content” android: layout_height = “wrap_content” android: layout_centerInParent = “adevarat” android: hapticFeedbackEnabled = “adevarat” android: src = “@ drawable / rewind_” app: backgroundTint = “@ android: color / white” app: fabSize = “mini” app: rippleColor = “# 225BC9FA” /> </RelativeLayout> <RelativLayout android: orientation = “horizontal” android: layout_width = “0dp” android : layout_height = “match_parent” android: layout_weight = “2” android: paddingLeft = “16dp” android: paddingStart = “16dp” android: clipToPadding = “false”> <android.support.design.widget.FloatingActionButton android: id = “@ + id / like_button “android: layout_width = android” wrap_content “: layout_height =” wrap_content “android: layout_centerVertical = android” adevarat “: layout_alignParentLeft =” adevarat “android: layout_alignParentStart =” adevarat “android: hapticFeedbackEnabled = true” true ” src = “@ drawable / like_green_24dp” app: backgroundTint = “@ android: color / white” app: fabSize = “auto” app: rippleColor = “# 226FE2B3” /> </RelativeLayout> </LinearLayout> <RelativeLayout Android: layout_width = android “match_parent”: layout_height = android “match_parent”: layout_above = “@ + id / button_container” android: padding = “20dp” android: clipToPadding = “false” android: clipChildren = “false”> <com.yuyakaido.android .cardstackview.CardStackView android: id = “@ + id / card_stack_view” android: layout_width = “match_parent” android: layout_height = “match_parent”> </com.yuyakaido.android.cardstackview.CardStackView> </RelativeLayout> </RelativeLayout> < / LinearLayout> <android.support.design.widget.NavigationView android: id = “@ + id / navigation_view” android: layout_width = “wrap_content” android: layout_height = “match_parent” android: layout_gravity = “start” android: egokitzenSystemWindows = ” true “> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout></com.yuyakaido.android.cardstackview.CardStackView> </RelativeLayout> </RelativeLayout> </LinearLayout> <android.support.design.widget.NavigationView android: id = “@ + id / navigation_view” android: layout_width = ” wrap_content “android: layout_height =” match_parent “android: layout_gravity =” start “android: egokitzenSystemWindows =” true “> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout></com.yuyakaido.android.cardstackview.CardStackView> </RelativeLayout> </RelativeLayout> </LinearLayout> <android.support.design.widget.NavigationView android: id = “@ + id / navigation_view” android: layout_width = ” wrap_content “android: layout_height =” match_parent “android: layout_gravity =” start “android: egokitzenSystemWindows =” true “> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>layout_gravity = “start” android: egokitzenSystemWindows = “true”> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>layout_gravity = “start” android: egokitzenSystemWindows = “true”> </android.support.design.widget.NavigationView> </android.support.v4.widget.DrawerLayout>

In continuare, vom crea interfata de utilizare a fiecarei carti de profil, precum si suprapunerea pe fiecare dintre ele, luand in considerare daca utilizatorul gliseaza spre stanga sau spre dreapta.

<? xml version = “1.0” coding = “utf-8”?> <! – https://qiita.com/ntsk/items/dac92596742e18470a55 -> <android.support.v7.widget.CardView xmlns: android = “http://schemas.android.com/apk/res/android” xmlns: app = “http://schemas.android.com/apk/res-auto” android: layout_width = android “match_parent”: layout_height = Android “match_parent”: background = “? attr / selectableItemBackground” android: foreground = “? attr / selectableItemBackground” app: cardUseCompatPadding = aplicatia “adevarata”: cardPreventCornerOverlap = “false” app: cardCornerRadius = “8dp” app: cardBackgroundColor = android: color / white “> <com.makeramen.roundedimageview.RoundedImageView android: id =” @ + id / item_image “android: layout_width =” match_parent “android: layout_height = “match_parent” android: scaleType = “centerCrop” app: riv_corner_radius = “8dp” /> <LinearLayout android: orientation = “vertical” android: layout_width = “match_parent” android: layout_height = “wrap_content” android: layout_gravity = Android “de jos”: padding = “16dp” android: background = “@ drawable / gradation_black”> <TextView android: id = “@ + id / item_name” android: layout_width = “wrap_content” android: layout_height = “wrap_content” android: textStyle = android “indraznet”: textColor = “@ android: color / white” android: textSize = “26sp” /> <TextView android: id = “@ + id / item_city” android: layout_width = “wrap_content” android: layout_height = “wrap_content”android: textStyle = “indraznet” android: textColor = “@ android: color / white” android: textSize = “20sp” /> </LinearLayout> <FrameLayout android: id = “@ + id / left_overlay” android: layout_width = ” match_parent “android: layout_height =” match_parent “android: background =” @ drawable / overlay_black “> <ImageView android: layout_width =” wrap_content “android: layout_height =” wrap_content “android: src =” @ drawable / skip_white_120dp “android: layout_gravity = “center” /> </FrameLayout> <FrameLayout android: id = “@ + id / right_overlay” android: layout_width = “match_parent” android: layout_height = “match_parent” android: background = “@ drawable / overlay_black”> <ImageView android : layout_width =“wrap_content “android: layout_height =” wrap_content “android: src =” @ drawable / like_white_120dp “android: layout_gravity =” center “/> </FrameLayout> <FrameLayout android: id =” @ + id / top_overlay “android: layout_width =” match_parent “android: layout_height =” match_parent “> </FrameLayout> <FrameLayout android: id =” @ + id / bottom_overlay “android: layout_width =” match_parent “android: layout_height =” match_parent “> </FrameLayout> </ android. support.v7.widget.CardView>layout_width = android “match_parent”: layout_height = “match_parent”> </FrameLayout> <FrameLayout android: id = “@ + id / bottom_overlay” android: layout_width = “match_parent” android: layout_height = “match_parent”> </FrameLayout> < /android.support.v7.widget.CardView>layout_width = android “match_parent”: layout_height = “match_parent”> </FrameLayout> <FrameLayout android: id = “@ + id / bottom_overlay” android: layout_width = “match_parent” android: layout_height = “match_parent”> </FrameLayout> < /android.support.v7.widget.CardView>

Aceasta este pentru UI, acum sa acoperim backend-ul.

Integrarea logicii aplicatiilor

Pentru ca aplicatia noastra sa fie completa, vom crea patru fisiere separate. Primul fisier de care vom avea nevoie este o clasa care va actiona ca obiect pentru fiecare profil si va contine informatiile aferente.

// fisier: Spot.kt clasa de date Spot (val id: Long = contor ++, nume var: String, val distanta: String, val url: String) {companie obiect {private var counter = 0L}}

In continuare, vom crea un fisier care va avea unele functii de ajutor pentru a actualiza colectia noastra de profiluri.

// SpotDiffCallback.kt import android.support.v7.util.DiffUtil clasa SpotDiffCallback (private val old: List <Spot>, private val new: List <Spot>): DiffUtil.Callback () {override fun getOldListSize (): Int {return old.size} anuleaza distractia getNewListSize (): Int {return new.size} anuleaza distractia suntItemsTheSame (oldPosition: Int, newPosition: Int): Boolean {return old [oldPosition] .id == new [newPosition] .id} Anuleaza distractia suntContentsTheSame (vechiPozitie: Int, nouaPozitie: Int): Boolean {return old [oldPosition] == new [newPosition]}}

Acum, putem incarca fiecare profil in frontend. Vom face acest lucru in cadrul unei clase numita CardStackAdapter.

// CardStackAdapter.kt import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.

porno mame vitrege http://intobooks.us/__media__/js/netsoltrademark.php?d=adult66.net/
parodie porno http://jameselskes.com/__media__/js/netsoltrademark.php?d=adult66.net/
porno cu nepoate http://mallpros.com/__media__/js/netsoltrademark.php?d=adult66.net/
mamica porno http://rieck.us/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/amatori
surori porno http://valsace.com/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/anal
porno free hd http://installwordpress.net/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/asiatice
poezii porno eminescu http://thewildcenter.net/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/beeg
porno muie http://iamaminority.com/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/blonde
porno in socks http://ideal-truevalue.com/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/brazzers
film porno gay http://aabonelli.com/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/brunete
filme porno cu subtitrare in romana http://sleepworldwide.com/__media__/js/netsoltrademark.php?d=adult66.net/filme-porno/chaturbate
filme porno cu doctori http://www.livemailer.nl/mail/redirect?pid=1933&mid=4381&email=fliermanweston.nl&goto=https://adult66.net/doi-studenti-amatori-fac-sex-nebun-in-pozitii-demne-de-filmele-porno
porno cu adolescente http://ezfilings.us/__media__/js/netsoltrademark.php?d=adult66.net/mama-sexy-isi-seduce-fiul-vitreg-apoi-il-fute-in-patul-conjugal
hentai porno http://www.wolowski.com/__media__/js/netsoltrademark.php?d=adult66.net/tanar-excitat-isi-fute-prietena-tare-de-tot-pe-podea
dowloand filme porno http://rjolaw.com/__media__/js/netsoltrademark.php?d=adult66.net/tanara-cu-un-fund-senzual-e-fututa-hard-pe-la-spate
filme porno cu abella danger http://femininephysique.com/__media__/js/netsoltrademark.php?d=adult66.net/papusica-blonda-filmata-de-prieten-in-timp-ce-ii-face-sex-oral
porno cu politiste http://libertyphysics.com/__media__/js/netsoltrademark.php?d=adult66.net/blonda-suge-pula-adanc-pana-cand-ejaculeaza-in-gura-ei
porno mama fiu http://www.hmconsultinggrp.com/__media__/js/netsoltrademark.php?d=adult66.net/doua-negrese-incinse-se-freaca-rau-una-de-alta-pana-au-orgasm
filme porno roanesti http://carmaxaccessories.org/__media__/js/netsoltrademark.php?d=adult66.net/tanara-timida-face-pentru-prima-oara-sex-oral
porno pizde http://courtmonitor.org/__media__/js/netsoltrademark.php?d=adult66.net/bunaciune-se-joaca-cu-bilele-de-cur-apoi-se-fute-in-draci

view.ViewGroup import android.widget.ImageView import android.widget.TextView import android.widget. Toast import com.bumptech.glide.Glide clasa CardStackAdapter (private var spots: List <Spot> = emptyList ()): RecyclerView.Adapter <CardStackAdapter.ViewHolder> () {override fun onCreateViewHolder (parent: ViewGroup, viewType: Int) ViewHolder {val inflater = LayoutInflater.from (parent.context) returnare ViewHolder (inflater.inflate (R.layout.item_spot, parent, false))} anuleaza distractia peBindViewHolder (titular: ViewHolder, pozitie: Int) {val spot = spot [ pozitie] Hold.name.text = “$ {spot.id}. $ {spot.name}” holder.city.text = spot.distance + “km” Glide.cu (Holder.image) .load (spot.url) .into (Hold.image) holder.itemView.setOnClickListener {v -> Toast.makeText (v.context, spot.name, Toast.LENGTH_SHORT) .show ()} } inlocuieste distractia getItemCount (): Int {return spots.size} fun setSpots (spoturi: List <Spot>) {this.spots = spot} fun getSpots (): List <Spot> {return spot} class ViewHolder (vizualizare: View ): RecyclerView.ViewHolder (view) {val name: TextView = view.findViewById (R.id.item_name) var city: TextView = view.findViewById (R.id.item_city) var image: ImageView = view.findViewById (R. id.item_image)}}) {this.spots = spot} fun getSpots (): Lista <Spot> {return spot} class ViewHolder (vizualizare: View): RecyclerView.ViewHolder (view) {val name: TextView = view.findViewById (R.id.item_name ) oras var: TextView = view.findViewById (R.id.item_city) var image: ImageView = view.findViewById (R.id.item_image)}}) {this.spots = spot} fun getSpots (): Lista <Spot> {return spot} class ViewHolder (vizualizare: View): RecyclerView.ViewHolder (view) {val name: TextView = view.findViewById (R.id.item_name ) var city: TextView = view.findViewById (R.id.item_city) var image: ImageView = view.findViewById (R.id.item_image)}}

Coase totul impreuna

Ne putem indrepta catre fisierul MainActivity.kt pentru a vedea cum totul se potriveste.

Haideti sa aruncam o privire rapida la metodele onCreate si onStart.

onCreate (salvatInstanceState) setContentView (R.layout.activity_main) val androidID = Settings.Secure.getString (this.contentResolver, Settings.Secure.ANDROID_ID) fusedLocationClient = LocationServices.getFusedLocationProviderClient (this) if (checkSelfPanification (this) .ACCESS_COARSE_LOCATION)! = PackageManager.PERMISSION_GRANTED) {if (ActivityCompat.shouldShowRequestPermissionRationale (acest lucru, android.Manifest.permission.ACCESS_COARSE_LOCATION)) {} else {ActivityCompat.requestPermissions (acest lucru, arrayOf (android.Manifest.permission.ACCESS_COARSE_LOCATION), MY_PERMISSIONS_REQUESTACCESS_COARSE_LOCATION) }} else {fusedLocationClient.lastLocation .addOnSuccessListener {location: Location? -> if (location! = null) {userLocation [0] = location.latitude userLocation [1] = location.longitude Log.d (“

Putem descompune tot ceea ce se intampla in trei lucruri.

In primul rand, vom obtine locatia dispozitivului folosind Locatia fuzibila. In continuare, ne vom abona la un canal cu acelasi nume ca ID-ul dispozitivului nostru, deoarece toate persoanele posibile pe care le putem glisa sunt publicate pe canalul respectiv. In sfarsit, in onStart, vom publica data referitoare la dispozitiv, la fel ca ID-ul si locatia. Motivul pentru care publicam in onStart si nu pe onCreate este pentru ca nu vom putea obtine toate informatiile pe care trebuie sa le publicam pana nu incepe activitatea.

Cu aceasta, sa adaugam toate functiile si folosind tastele pub / sub (in tabloul de bord al administratorului), in MainActivity. In final, fisierul nostru va arata astfel:

onCreate (salvatInstanceState) setContentView (R.layout.activity_main) val androidID = Settings.Secure.getString (this.contentResolver, Settings.Secure.ANDROID_ID) fusedLocationClient = LocationServices.getFusedLocationProviderClient (this) if (checkSelfPanification (this) .ACCESS_COARSE_LOCATION)! = PackageManager.PERMISSION_GRANTED) {if (ActivityCompat.shouldShowRequestPermissionRationale (acest lucru, android.Manifest.permission.ACCESS_COARSE_LOCATION)) {} else {ActivityCompat.requestPermissions (acest lucru, arrayOf (android.Manifest.permission.ACCESS_COARSE_LOCATION), MY_PERMISSIONS_REQUESTACCESS_COARSE_LOCATION) }} else {fusedLocationClient.lastLocation .addOnSuccessListener {location: Location? -> if (location! = null) {userLocation [0] = location.latitude userLocation [1] = location.longitude Log.d (”

Hai sa rulam aplicatia! Intr-un emulator sau pe un dispozitiv, puteti vedea functionalitatea de glisare, precum si distanta utilizatorului fata de dvs.

Buna treaba! Doriti sa explorati mai multe functii si idei in jurul aplicatiilor de intalnire pentru mobil? Vedeti prezentarea noastra de aplicatii de intalnire in timp real si vedeti cum puteti alimenta aplicatii de intalniri multipla, rapide si sigure la scara globala cu API-urile de chat si infrastructura de mesagerie PubNub.