Poly API: recuperació de recursos 3D per a les aplicacions Android i VR amb AR

Autora: Peter Berry
Data De La Creació: 14 Lang L: none (month-012) 2021
Data D’Actualització: 4 Ser Possible 2024
Anonim
Poly API: recuperació de recursos 3D per a les aplicacions Android i VR amb AR - Aplicacions
Poly API: recuperació de recursos 3D per a les aplicacions Android i VR amb AR - Aplicacions

Content


Tens una idea fantàstica per a una aplicació mòbil de Realitat Virtual (VR) o Reality Augmented (AR), però no tens idea de com donar vida a la teva visió?

Tret que no siguis un desenvolupador d’Android que també sigui un artista en 3D amb experiència, la creació de tots els actius necessaris per oferir una experiència immersiva de 360 ​​graus, pot ser un procés desconcertant.

Només perquè no disposeu del temps, recursos o experiència necessaris per crear models 3D, no ho fa vol dir que no podeu crear una aplicació per a mòbils VR o AR fantàstica. Hi ha una àmplia gamma de recursos en 3D disponibles lliurement a la World Wide Web, a més de totes les API, marcs i biblioteques que necessiteu per descarregar i renderitzar aquests actius a les aplicacions d'Android.

Llegiu Següent: Ara podeu visitar qualsevol lloc web amb Daydream VR. Fins i tot aquell.


En aquest article, anem a veure Poly, un repositori en línia i API que posa milers d'actius 3D al vostre abast. Al final d’aquest article, haureu creat una aplicació que recuperi un recurs 3D Poly en temps d’execució i, a continuació, la rendibilitzeu fent servir el popular processat de la biblioteca Android.

Es mostren els recursos 3D amb Poly

Si mai heu treballat en desenvolupament Unity, el dipòsit Poly és semblant al magatzem d’actius Unity, excepte que tot el que és Poly és gratuït!

Molts dels models 3D de Poly es publiquen sota la llicència Creative Commons, de manera que no sou lliure d’utilitzar, modificar i remixar aquests actius, sempre que concediu al creador un crèdit adequat.

Tots els models 3D de Poly estan dissenyats per ser compatibles amb les plataformes VR i AR de Google, com ara Daydream i ARCore, però podeu utilitzar-les allà on vulgueu, fins i tot, podríeu utilitzar-les amb ARKit d'Apple.


Quan es tracta de recuperar i mostrar els actius Poly, teniu dues opcions. Primerament, podeu descarregar els recursos al vostre ordinador i després importar-los a Android Studio, de manera que s’envien amb la vostra aplicació i contribueixen a la seva mida APK, o podeu recuperar aquests recursos en temps de funcionament mitjançant l’API Poly.

La polivalent basada en REST, basada en REST, proporciona accés programàtic i de només lectura a la gran col·lecció de models 3D de Poly. Això és més complicat que agrupar recursos amb el vostre APK, però hi ha diversos avantatges per recuperar els recursos Poly al llarg del temps, sobretot que contribueix a mantenir el control de la mida del vostre APK, cosa que pot afectar la quantitat de persones que descarregueu l'aplicació.

També podeu utilitzar l’API poli per oferir més opció als vostres usuaris, per exemple, si esteu desenvolupant un joc per a mòbils, podríeu deixar als vostres usuaris triar entre diversos models de caràcters.

Com que no podeu modificar els models Poly, podríeu deixar els usuaris ajustar el caràcter escollit, per exemple, modificant el color del cabell o dels ulls o combinant-lo amb altres actius Poly, com ara armes i armadures diferents. D’aquesta manera, l’API Poly us pot ajudar a oferir una gamma impressionant d’actius en 3D, amb molt d’abast per personalitzar l’experiència, i tot això per a un treball relativament poc limitat. Els vostres usuaris estaran convençuts que heu dedicat un munt de temps a elaborar meticulosament tots aquests models 3D.

Creació d’un projecte de modelització 3D

Crearem una aplicació que recuperi un recurs Poly particular quan s’iniciï per primera vegada l’aplicació i, després, a sol·licitud de l’usuari, es visualitzarà aquest recurs en mode de pantalla completa.

Per ajudar-nos a recuperar aquest recurs, utilitzaré Fuel, que és una biblioteca de xarxes HTTP per a Kotlin i Android. Comenceu per crear un projecte nou amb la configuració que escolliu, però quan us demanin, opteu per "Incloure suport Kotlin".

Totes les trucades que feu a l’API poli han d’incloure una clau d’API, que s’utilitza per identificar la vostra aplicació i fer complir els límits d’ús. Durant el desenvolupament i les proves, sovint fareu servir una clau d’API sense restriccions, però si teniu previst alliberar aquesta aplicació, haureu d’utilitzar una clau d’API restringida a Android.

Per crear una clau restringida, haureu de conèixer el certificat de signatura SHA-1 del vostre projecte, així que obtenim aquesta informació ara:

  • Seleccioneu la pestanya "Gradle" d'Android Studio (on es troba el cursor a la captura de pantalla següent). S'obre un plafó "Projectes Gradle".

  • Al tauler "Projecta Gradle", feu doble clic per ampliar la "arrel" del vostre projecte i, a continuació, seleccioneu "Tasques> Android> Informe de signatura". Obre un nou panell a la part inferior de la finestra d'Android Studio.
  • Seleccioneu el botó "Commuta les execucions de tasques / mode de text" (on es troba el cursor a la captura de pantalla següent).

El tauler "Executar" s'actualitzarà per mostrar molta informació sobre el vostre projecte, inclosa la seva empremta digital SHA-1.

Creeu un compte de Google Cloud Platform

Per adquirir la clau d’API necessària, necessitareu un compte de Google Cloud Platform (GPC).

Si no teniu cap compte, podeu registrar-vos per a una prova gratuïta de 12 mesos dirigint-vos a la pàgina de prova de Cloud Cloud de forma gratuïta i seguint les instruccions. Tingueu en compte que és necessària una targeta de crèdit o una targeta de dèbit, però, segons la pàgina de preguntes freqüents, només es fa servir per verificar la vostra identitat i "no se't cobrarà ni facturarà durant la prova gratuïta."

Obteniu la vostra clau d’API poli

Un cop hàgiu registrat, podeu activar l’API poli i crear la vostra clau:

  • Dirigiu-vos a la consola GCP.
  • Seleccioneu la icona alineada a la cantonada superior esquerra i trieu "API i serveis> Tauler de control".
  • Seleccioneu "Activa API i serveis".
  • Al menú de l'esquerra, seleccioneu "Altres".
  • Seleccioneu la targeta "API" poli.
  • Feu clic al botó "Habilitar".
  • Al cap d’uns moments, sereu conduïts a una nova pantalla; obriu el menú lateral i trieu "API i serveis> Credencials".

  • A la finestra emergent posterior, seleccioneu "Restringir la clau".
  • Poseu un nom distintiu a la vostra clau.
  • A "Restriccions d'aplicació", seleccioneu "Aplicacions d'Android".
  • Seleccioneu "Afegir nom del paquet i empremta digital".
  • Copieu / enganxeu l’empremta digital SHA-1 del vostre projecte al camp “Empremta digital del certificat de signatura”.
  • Introduïu el nom del paquet del vostre projecte (apareix al manifest i a la part superior de cada fitxer de classe).
  • Feu clic a "Desa".

Ara se us dirigirà a la pantalla "Credencials" del vostre projecte, que conté una llista de totes les vostres claus de l'API, inclosa la clau d'API poli-habilitada que acabeu de crear.

Dependències del projecte: extensions de combustible, P3D i Kotlin

Per recuperar i mostrar els actius poli, necessitarem una ajuda d’algunes biblioteques addicionals:

  • Combustible. Actualment, Poly no disposa de cap equip d’eines oficial per a Android, per la qual cosa haureu de treballar directament amb l’API mitjançant la seva interfície REST. Per fer aquest procés més senzill, faré servir la biblioteca de xarxa HTTP de combustible.
  • Processament per a Android. Faré servir el render P3D d’aquesta biblioteca per mostrar l’actiu Poly.

Obre el fitxer build.gradle del projecte i afegeix aquestes dues biblioteques com a dependències del projecte:

dependencies {implement fileTree (inclou:, dir: libs) implementació "org.jetbrains.kotlin: kotlin-stdlib-jre7: $ kotlin_version" implementació com.android.support:appcompat-v7:27.1.1 // Afegeix la biblioteca de combustible / / implementació com.github.kittinunf.fuel: fuel-android: 1.13.0 // Afegir el motor de processament per a Android // implementació org.p5android: processing-core: 4.0.1}

Per fer el nostre codi més concis, també faré servir extensions Android de Kotlin, així que afegim aquest complement mentre tinguem obert el fitxer build.gradle:

apply plugin: kotlin-android-extensions

Finalment, com que recuperem l’actiu d’Internet, la nostra aplicació necessita el permís d’Internet. Obriu el vostre manifest i afegiu el següent:

Afegir la clau d’API

Cada vegada que la nostra aplicació sol·licita un recurs de Poly, ha d’incloure una clau d’API vàlida. Estic fent servir un marcador de posició, però tu haver de substituïu aquest marcador de posició per la vostra pròpia clau d’API si l’aplicació mai funciona.

També afegiré un xec, de manera que l’aplicació mostrarà una advertència si oblideu substituir el text “INSERT-LA TUA-API-KEY”:

import android.os.Bundle import android.support.v7.app.AppCompatActivity classe MainActivity: AppCompatActivity () {company object {const val APIKey = "INSERT-YOUR-API-KEY"} anul·la la diversió onCreate (savedInstanceState: Bundle?) { super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // Si la clau API comença amb "INSERT" ... // si (APIKey.startsWith ("INSERT")) {// mostra el següent brindis ... .// Toast.makeText (això, "No heu actualitzat la vostra clau d’API", Toast.LENGTH_SHORT) .show ()} else {... ... ...

Recuperació del bé

Podeu triar qualsevol recurs al lloc de Google Poly, però utilitzaré aquest model de planeta Terra.

Recupereu un recurs mitjançant el seu identificador, que apareix al final de la banda URL (ressaltat a la captura de pantalla anterior). Combinem aquest identificador d’actius amb l’amfitrió de l’API Poly, que és “https://poly.googleapis.com/v1”.

importa android.content.Intent importació android.os.Bundle import android.support.v7.app.AppCompatActivity import import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.httpGet import kotlinx.android.synthetic.main.activity_main. * importa la classe java.io.File MainActivity: AppCompatActivity () {objecte company {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL =" https://poly.googleapis.com/v1/assets/94XG1XUy10q "} anul·len la diversió onCreate (savedInstanceState: Bundle?) {Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ("INSERT")) {Toast.makeText (això, "No heu actualitzat la vostra clau d'API", Toast.LENGTH_SHORT) .show ()} else {

A continuació, hem de fer una sol·licitud GET a l’URL de l’actiu, mitjançant el mètode httpGet (). També estic especificant que el tipus de resposta ha de ser JSON:

importa android.content.Intent importació android.os.Bundle import android.support.v7.app.AppCompatActivity import import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.httpGet import kotlinx.android.synthetic.main.activity_main. * importa la classe java.io.File MainActivity: AppCompatActivity () {objecte company {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL =" https://poly.googleapis.com/v1/assets/94XG1XUy10q "} anul·len la diversió onCreate (savedInstanceState: Bundle?) {Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ("INSERT")) {Toast.makeText (això, "No heu actualitzat la vostra clau API", Toast.LENGTH_SHORT) .show ()} else {// Feu una trucada al servidor i, a continuació, passeu les dades mitjançant la Mètode "listOf" // assetURL.httpGet (listOf ("clau" per APIKey)). ResponseJson {request, response, result -> // Feu alguna cosa amb la resposta // result.fold ({val as set = it.obj ()

L'actiu pot tenir diversos formats, com ara OBJ, GLTF i FBX. Hem de determinar que l’actiu té el format OBJ.

En aquest pas, també recuperaré el nom i l’URL de tots els fitxers que hem de descarregar,
inclòs el fitxer principal de l’actiu (“root”), a més de qualsevol fitxer de material i textura associat (“recursos”).

Si la nostra aplicació no pot recuperar correctament l’actiu, mostrarà un brindis informant a l’usuari.

importa android.content.Intent importació android.os.Bundle import android.support.v7.app.AppCompatActivity import import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.httpGet import kotlinx.android.synthetic.main.activity_main. * importa la classe java.io.File MainActivity: AppCompatActivity () {objecte company {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL =" https://poly.googleapis.com/v1/assets/94XG1XUy10q "} anul·len la diversió onCreate (savedInstanceState: Bundle?) {Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ("INSERT")) {Toast.makeText (això, "No heu actualitzat la vostra clau API", Toast.LENGTH_SHORT) .show ()} else {// Feu una sol·licitud GET a l'URL de l’actiu // assetURL. httpGet (listOf ("clau" per APIKey)). responseJson {request, response, result -> // Fes alguna cosa amb la resposta // result.fold ({val asset = it.obj () var objectURL: String? = null var materialLibraryName: Cadena? = null var materialLibraryURL: Cadena? = null // Comproveu el format de l'actiu, utilitzant la matriu "formats" // val assetFormats = asset.getJSONArray ("formats") // Enllaçar tots els formats // for (i en 0 fins a assetFormats.length ()) { val currentFormat = assetFormats.getJSONObject (i) // Utilitzeu formatType per identificar el tipus de format d'aquest recurs. Si el format és OBJ ... .// if (currentFormat.getString ("formatType") == "OBJ") {//... expandir recuperar el fitxer 'root' d'aquest recurs, és a dir, el fitxer OBJ // objectURL = currentFormat. getJSONObject ("root") .getString ("url") // Recupera totes les dependències del fitxer arrel // materialLibraryName = currentFormat.getJSONArray ("recursos") .getJSONObject (0) .getString ("relativePath") materialLibraryURL = currentFormat.getJSONAr ("recursos") .getJSONObject (0) .getString ("url") ruptura}} objectURL !! httpDownload (). destination {_, _ -> File (filesDir, "globeAsset.obj")} .response {_ , _, result -> result.fold ({}, {// Si no podeu localitzar o descarregar el fitxer OBJ, mostreu un error // Toast.makeText (això, "No es pot descarregar el recurs", Toast.LENGTH_SHORT ) .show ()})} materialLibraryURL !!. httpDownload (). destination {_, _ -> File (filesDir, materialLibraryName)} .response {_, _, result -> result.fold ({}, {Toast. makeText (això, "No es pot descarregar el recurs", Toast.LENGTH_SHORT) .show ()})}}, { Toast.makeText (aquest, "No es pot descarregar el recurs", Toast.LENGTH_SHORT) .show ()})}}}

En aquest moment, si instal·leu el projecte al telèfon intel·ligent o a la tauleta Android o al dispositiu virtual Android (AVD), l'actiu es descarregarà correctament, però l'aplicació no el mostrarà realment. Fixem-ho ara!

Creació d'una segona pantalla: afegir navegació

Mostrarem l’actiu en mode de pantalla completa, per tant, actualitzem el nostre fitxer main_activity.xml per incloure un botó que, quan toqueu, llançarà l’Activitat de pantalla completa.

Ara anem a afegir l’onClickListener al final del fitxer MainActivity.kt:

importa android.content.Intent importació android.os.Bundle import android.support.v7.app.AppCompatActivity import import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.httpGet import kotlinx.android.synthetic.main.activity_main. * importa la classe java.io.File MainActivity: AppCompatActivity () {objecte company {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL =" https://poly.googleapis.com/v1/assets/94XG1XUy10q "} anul·len la diversió onCreate (savedInstanceState: Bundle?) {Super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ("INSERT")) {Toast.makeText (això, "No heu actualitzat la vostra clau d'API", Toast.LENGTH_SHORT) .show ()} else {assetURL.httpGet (listOf ("clau" per APIKey)). responseJson {request, response, result -> result.fold ({val asset = it.obj () var objectURL: String? = null var materialLibraryName: String? = null var materialLibraryURL: Str inger? = null val assetFormats = asset.getJSONArray ("formats") per a (i en 0 fins a assetFormats.length ()) {val currentFormat = assetFormats.getJSONObject (i) if (currentFormat.getString ("formatType") == "OBJ" ) {objectURL = currentFormat.getJSONObject ("root") .getString ("url") materialLibraryName = currentFormat.getJSONArray ("recursos") .getJSONObject (0) .getString ("relativePath") materialLibraryURL = currentFormat.getJSONArray ) .getJSONObject (0) .getString ("url") break}} objectURL !! httpDownload (). destination {_, _ -> File (filesDir, "globeAsset.obj")} .response {_, _, resultat -> result.fold ({}, {Toast.makeText (aquest, "No es pot descarregar el recurs", Toast.LENGTH_SHORT) .show ()})} materialLibraryURL !!. httpDownload (). destination {_, _ -> File (filesDir, materialLibraryName)} .response {_, _, result -> result.fold ({}, {Toast.makeText (això, "No es pot descarregar el recurs", Toast.LENGTH_SHORT) .show ()})}}, {Toast.makeText (aquest, "No es pot descarregar el recurs", Toast.LENGTH_SHORT) .sh ow ()})} // Implementar un botó // displayButton.setOnClickListener {val intent = Intent (això, SecondActivity :: class.java) startActivity (intent); }}}

Construcció d'un llenç 3D

Ara, creem l’activitat on mostrarem el nostre actiu en mode de pantalla completa:

  • Feu clic sobre el fitxer MainActivity.kt del vostre projecte i seleccioneu "Nou> Classe / Fitxer Kotlin".
  • Obriu el menú desplegable "Tipus" i seleccioneu "Classe".
  • Poseu-li el nom de "Segona activitat" a aquesta classe i feu clic a "D'acord".

Per dibuixar un objecte 3D, necessitem un llenç 3D. Utilitzaré el renderitzador P3D de Processing per a la biblioteca d’Android, la qual cosa significa estendre la classe PApplet, superar el mètode de configuració () i després passar P3D com a argument al mètode de pantalla completa (). També hem de crear una propietat que representi l’actiu Poly com a objecte PShape.

private fun displayAsset () {val canvas3D = objecte: PApplet () {var polyAsset: PShape? = null anul·la la configuració de diversió () {pantalla completa (PConstants.P3D)}

A continuació, hem d’inicialitzar l’objecte PShape, substituint el mètode setup (), anomenant el mètode loadShape (), i després passant la ruta absoluta del fitxer .obj:

Anul·la la configuració divertida () {polyAsset = loadShape (Fitxer (filesDir, "globeAsset.obj"). absolutPath)}

Es basa en el llenç de P3D

Per dibuixar aquest llenç 3D, hem de substituir el mètode draw ():

Substitueix el sorteig de diversió () {forma (fons) (0) (fons polivalent)}}

De manera predeterminada, molts dels recursos recuperats de l’API Poly es troben en el costat més reduït, de manera que si s’executa aquest codi ara, és possible que ni tan sols vegi l’actiu, segons la configuració de la pantalla. Quan creeu escenes en 3D, normalment creareu una càmera personalitzada perquè l'usuari pugui explorar l'escena i veure els vostres recursos en 3D des dels 360 graus. Tanmateix, aquest article està fora de l’àmbit d’aquest article, de manera que canviaré la mida i la posició de l’actiu manualment, per assegurar-me que s’ajusti còmodament a la pantalla.

Podeu augmentar la mida de l’actiu, passant un valor negatiu al mètode scale ():

escala (-10f)

Podeu ajustar la posició de l’actiu a l’espai virtual 3D mitjançant el mètode translate () i les coordenades següents:

  • X. Situa l’actiu al llarg de l’eix horitzontal.
  • I Situa l’actiu al llarg de l’eix vertical.
  • Z. Aquest és l’eix “profunditat / alçada”, que transforma un objecte 2D en un objecte 3D. Els valors positius creen la impressió que l’objecte s’acosta cap a tu i els valors negatius creen la impressió que l’objecte s’allunya de tu.

Tingueu en compte que les transformacions són acumulatives, de manera que tot el que succeeix després de la funció acumula l'efecte.

Estic fent servir el següent:

traduir (-50f, -100f, 10f)

Aquí teniu el codi completat:

Anul·lar l'atracció de diversió () {background (0) scale (-10f) translate (-50f, -100f) // Dibuixar l'actiu cridant el mètode shape () // forma (polyAsset)}}

A continuació, hem de crear el fitxer de disseny corresponent, on afegirem el llenç 3D com a widget FrameLayout:

  • Feu clic amb el botó de control a la carpeta "res> disseny" del vostre projecte.
  • Seleccioneu "Fitxer de recurs de maquetació".
  • Poseu aquest nom al fitxer "activitat_segona" i, a continuació, feu clic a "D'acord".

Ara tenim el nostre "frame_view" FrameLayout, hem de donar a conèixer el nostre SecondActivity. Torneu-vos al fitxer SecondActivity.kt, creeu una instància PFragment nova i apunteu-la en direcció al nostre widget "asset_view":

importar android.os.Bundle importar android.support.v7.app.AppCompatActivity importar kotlinx.android.synthetic.main.activity_second. * importar process.android.PFragment importar processament.core.PApplet importar processament.core.PConstants importar processament.core .PShape importa classe java.io.File SecondActivity: AppCompatActivity () {anul·la la diversió onCreate (savedInstanceState: Bundle?) {Super.onCreate (savedInstanceState) setContentView (R.layout.activity_second) displayAsset ()} private fun displayAsset ()} private fun displayAsset () canvas3D = object: PApplet () {var polyAsset: PShape? = null override fun settings () {fullscreen (PConstants.P3D)} anul·la la configuració de la diversió () {polyAsset = loadShape (Fitxer (filesDir, "globeAsset.obj"). absolutePath)} anul·la la diversió del dibuix () {background (0) escala (-10f) traduir la forma (-50f, -100f) (polyAsset)}} // Afegiu el següent // val assetView = PFragment (canvas3D) assetView.setView (asset_view, this)}}

El darrer pas, és afegir SecondActivity al manifest:

// Afegiu el següent //

Prova del vostre projecte

Ja estem preparats per provar el projecte acabat. Instal·leu-lo al dispositiu Android o a l'AVD i assegureu-vos que teniu una connexió a Internet activa. Tan aviat com es llanci l’aplicació, baixarà l’actiu i, a continuació, podreu visualitzar-lo donant-li un toc al botó “Visualitzar l’actiu”.

Podeu descarregar aquest projecte complet de GitHub.

Embalatge

En aquest article, vam analitzar com utilitzar l'API Poly per recuperar un recurs 3D en temps d'execució i com mostrar aquest recurs mitjançant la biblioteca Processament per a Android. Creus que la API de Poly pot potenciar el desenvolupament de VR i AR per a més gent? Feu-nos-ho saber als comentaris a continuació!

Relacionat

  • Google portarà les aplicacions AR a "centenars de milions" de dispositius Android el 2018
  • Google t’ensenyarà de forma gratuïta a l’IA i l’aprenentatge automàtic
  • 15 millors jocs VR per a Google Cardboard
  • 10 millors aplicacions VR per a Google Cardboard
  • Què és Google Fuchsia? És el nou Android?
  • Què és Google Duplex? - funcions, data de llançament i molt més
  • Com es pot crear una aplicació VR per a Android en només 7 minuts
  • Auriculars VR mòbils: quines són les millors opcions?

La indútria de l'entreteniment per a adult ha recorregut un llarg camí en el darrer 20 any. Aban, nomé teníeu cinte VH amb poca il·luminació. O a le revite que hav&#...

Tot i que la tecnologia del telèfon intel·ligent ha avançat ignificativament de del primer die, la durada de la bateria no ha etat així. Tenir un dia complet d’ú del telè...

Popular Avui