Volley es una biblioteca HTTP que facilita y agiliza el acceso a Internet en aplicaciones nativas en Android.
Permite administrar respuestas con formato strings, JSON e imágenes.
La manera de incorporar Volley en un proyecto es agregar la siguiente dependencia al archivo build.gradle de la aplicación:
dependencies { ... implementation 'com.android.volley:volley:1.2.0' }
Nuestra aplicación debe acceder a internet por lo que debemos pedir dicho permiso en el archivo AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
El sitio de videos dailymotion.com provee una API para recuperar una serie de datos de sus videos.
El API tiene muchos parámetros para filtrar los videos que queremos acceder (país, duración, etc.), emplearemos la más sencilla:
https://api.dailymotion.com/videos?limit=100
Nos retorna un archivo JSON con el siguiente formato:
Luego sabemos también que se nos provee una imagen en miniatura del video con la siguiente dirección:
https://www.dailymotion.com/thumbnail/video/[atributo id almacenado en el archivo JSON]
Si queremos acceder al sitio directamente de 'dailymotion.com' para ver un video en particular podemos hacerlo con la URL:
https://www.dailymotion.com/video/[atributo id almacenado en el archivo JSON]
La aplicación debe recuperar el id, título y canal. Mostrar una lista de tarjetas con el título, canal y la miniatura de la imagen, si se hace clic sobre la tarjeta se debe abrir el navegador y mostrar el video respectivo.
Crearemos el proyecto Compose21
Agregamos el permiso para que nuestra aplicación pueda acceder a internet en el archivo AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tutorialesprogramacionya.basura46"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Basura46"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.Basura46.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Agregamos las dependencias de la biblioteca Volley y coil-kt (es una biblioteca de carga de imágenes para Android respaldada por Kotlin Coroutines):
dependencies { implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'androidx.activity:activity-compose:1.3.1' implementation 'com.android.volley:volley:1.2.0' implementation("io.coil-kt:coil-compose:1.3.2") testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" }
Debemos obtener un resultado similar a:
El código fuente que debemos implementar es:
package com.tutorialesprogramacionya.compose21 import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.rememberImagePainter import com.android.volley.Request import com.android.volley.toolbox.JsonObjectRequest import com.android.volley.toolbox.Volley class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ListadoVideos(this) setContent { PantallaPrincipal() } } } data class Video(val id: String, val titulo: String, val canal: String) val videos = mutableStateListOf<Video>() fun ListadoVideos(activity: Activity) { val url = "https://api.dailymotion.com/videos?limit=100" val requestQueue = Volley.newRequestQueue(activity) val jsonObjectRequest = JsonObjectRequest( Request.Method.GET, url, null, { response -> val jsonArray = response.getJSONArray("list") videos.clear() for (i in 0 until jsonArray.length()) { val registro = jsonArray.getJSONObject(i) val id = registro.getString("id") val titulo = registro.getString("title") val canal = registro.getString("channel") videos.add(Video(id, titulo, canal)) } }, { error -> } ) requestQueue.add(jsonObjectRequest) } @Composable fun PantallaPrincipal() { val activity = LocalContext.current LazyColumn() { items(videos) { video -> Card( elevation = 5.dp, modifier = Modifier .padding(10.dp) .fillMaxWidth() .clickable { val intento = Intent( Intent.ACTION_VIEW, Uri.parse("https://www.dailymotion.com/video/${video.id}") ) activity.startActivity(intento) } ) { Column() { Text( text = "Título: ${video.titulo}", fontSize = 18.sp, modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 5.dp) ) Text( text = "Canal:${video.canal}", modifier = Modifier.padding(10.dp) ) Image( painter = rememberImagePainter("https://www.dailymotion.com/thumbnail/video/${video.id}"), contentDescription = null, modifier = Modifier.size(350.dp, 200.dp) ) } } } } }
Desde el método onCreate llamamos a la función ListadoVideos (no es una función composable):
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ListadoVideos(this) setContent { PantallaPrincipal() } } }
Definimos un data class para luego definir una serie de objeto de dicha clase y almacenarlos en la variable videos, que al ser de tipo mutableStateListOf hace que cuando se modifique, Compose se encargue de actualizar la pantalla donde se haga referencia a la misma:
data class Video(val id: String, val titulo: String, val canal: String) val videos = mutableStateListOf<Video>()
La función ListadoVideos procede a crear un objeto de la clase JsonObjectRequest, dicha clase encapsula la petición HTTP y mediante getJSONArray parseamos el archivo recibido para recuperar cada elemento del arreglo.
Con cada objeto almacenado en el archivo Json procedemos a crear ahora un objeto de la clase Video y almacenarlo posteriormente en la variable 'videos' (esto hará que se actualice la pantalla):
fun ListadoVideos(activity: Activity) { val url = "https://api.dailymotion.com/videos?limit=100" val requestQueue = Volley.newRequestQueue(activity) val jsonObjectRequest = JsonObjectRequest( Request.Method.GET, url, null, { response -> val jsonArray = response.getJSONArray("list") videos.clear() for (i in 0 until jsonArray.length()) { val registro = jsonArray.getJSONObject(i) val id = registro.getString("id") val titulo = registro.getString("title") val canal = registro.getString("channel") videos.add(Video(id, titulo, canal)) } }, { error -> } ) requestQueue.add(jsonObjectRequest) }
Por último nuestra función composable llama a la función LazyColumn y con la función items recuperamos cada objeto almacenado en la variable 'videos'.
Creamos una tarjeta (Card) por cada video, y llamando a la función Text dos veces mostramos el título y el canal del video.
Para mostrar la imagen del video llamamos a la función composable 'rememberImagePainter' que se encuentra en la biblioteca:
implementation("io.coil-kt:coil-compose:1.3.2")
Por últimos cuando se hace clic en la tarjeta procedemos a crear un objeto de la clase Intent y abrimos el navegador con la dirección donde se encuentra el video propiamente dicho:
@Composable fun PantallaPrincipal() { val activity = LocalContext.current LazyColumn() { items(videos) { video -> Card( elevation = 5.dp, modifier = Modifier .padding(10.dp) .fillMaxWidth() .clickable { val intento = Intent( Intent.ACTION_VIEW, Uri.parse("https://www.dailymotion.com/video/${video.id}") ) activity.startActivity(intento) } ) { Column() { Text( text = "Título: ${video.titulo}", fontSize = 18.sp, modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 5.dp) ) Text( text = "Canal:${video.canal}", modifier = Modifier.padding(10.dp) ) Image( painter = rememberImagePainter("https://www.dailymotion.com/thumbnail/video/${video.id}"), contentDescription = null, modifier = Modifier.size(350.dp, 200.dp) ) } } } } }
Este proyecto lo puede descargar en un zip desde este enlace: Compose21.zip