SlideShare una empresa de Scribd logo
1 de 64
TV Future is Apps
tvOS vs AndroidTV
by @pablito_az
@hugojperal
@jr_salazares
MADRID · NOV 18-19 · 2016
Agenda
¿Quiénes somos?
Introducción a la plataforma
Dinámica del workshop.
Primeros pasos
¿Cómo buscar contenido dentro de la app?
Vayamos al detalle.
Cosas que nos dejamos en el tintero
¿Quiénes somos?
http://www.edreamsodigeocareers.com
Introducción. Experiencia
Introducción. Interacción
Introducción. UX
Introducción. Foco
Dinámica del workshop
Repo AndroidTV: https://github.com/odigeoteam/AndroidTV-workshop
Repo tvOS y TVML: https://github.com/odigeoteam/tvOS-workshop
Primeros pasos
Step 1 - 2
Para que sea compatible en AndroidTV ...
… tenemos que hacer algunas cosas:
● Nuestras activity principal CATEGORY_LEANBACK_LAUNCHER
● <uses-feature android:name = “android.software.leanback”
android:required = “false”>
● <uses-feature android:name = “android.hardware.touchscreen”
android:required = “false”>
… y nuestro dispositivo tiene limitaciones:
● Touchscreen
● Telephony
● Camera
● GPS
● Microphone
● Sensors
Adaptando el comportamiento ...
… en tiempo de ejecución:
if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
// Hago algo con la cámara
} else {
// Adapto mi comportamiento
}
Algunas características de los layouts
Debemos tener en cuenta:
● Layouts en Landscape.
● UI en secciones fácilmente navegables.
● Suficiente margen entre elementos.
● Cuidado con el Overscan
● Textos suficientemente visibles.
v17 Leanback to the rescue
compile 'com.android.support:leanback-v17:24.2.1'
Nuestra primera vista: BrowseFragment
Configurando el BrowseFragment
Podemos configurar elementos en la interfaz muy fácil:
setBadgeDrawable(...);
setOnSearchClickedListener(...);
setSearchAffordanceColor(...);
Pintando contenido
Pintando contenido
class ContentAdapter extends ArrayObjectAdapter {
for (CategoryList) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new
CardItemPresenter());
for (awesomePlaces) {
listRowAdapter.add(places);
}
HeaderItem header = new HeaderItem(Category);
add(new ListRow(header, listRowAdapter));
}
}
Nuestro CardItemPresenter
public class CardItemPresenter extends Presenter{
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
// Creo mi ImageCardView aquí
}
@Override
public void onBindViewHolder(ViewHolder viewHolder,
Object item) {
// Pinto la información en la ImageCardView
}
}
Configurando ImageCardView
Tiene 3 elementos principales, además de la imágen de fondo:
Se pueden configurar, mediante un Style combinando estos
elementos:
<item name="lbImageCardViewType">Title|Content|IconOnLeft</item>
● Título
● Contenido
● Badge
Step 1 - 2
Limitaciones
200MB
No LocalStorage
No WebViews
No Custom Video Player
No Contacts, HandOff...
Universal Purchase
Misma entrada en iTunes Connect
Mismo bundle ID
Subidas a iTunes Connect separadas
Distintas versiones
Distintos procesos de revisión
Esta unión es para… siempre
Construyamos un layout
Grids, grids everywhere
¿puedo tener el foco? -> canBecomeFocused
UIButton, UIControl, UISegmentedControl, UITabBar, UITextField, UISearchBar(UITextField)
collectionView(_:canFocusItemAtIndexPath:)
tableView(_:canFocusRowAtIndexPath:)
y yo, ¿tengo el foco? -> focused
didUpdateFocusInContext(_:withAnimationCoordinator:)
Step 1-2
TVML
TVML (TV Markup Language)
TVJS (TV Javascript)
TVMLKit
TVML
Templates, templates everywhere
18 templates a elegir
Elementos simples
Elementos compuestos
carousel, collectionList, shelf,
lockup
TVML
JS Principales
application.js:
JS Inicial. Carga otros JS y la primera pantalla
DocumentLoader:
Encargado de cargar documentos
DocumentController:
Es el controller y tiene asociado un loader y un
template. Nos abstrae de la navegación
TVML
Aspecto template
● Para navegar entre pantallas
usaremos el atributo
“documentURL” en cualquier
elemento focusable
TVML
Step 1 - 2 TVML
¿Cómo buscamos dentro de la app?
Step 3
In-app search: SearchFragment
SearchFragment
class SearchViewImp extends SearchFragment implements SearchResultProvider {
@Override
public ObjectAdapter getResultsAdapter() {
}
@Override
public boolean onQueryTextChange(String newQuery) {
}
@Override
public boolean onQueryTextSubmit(String query) {
}
}
Cards interaction: BackgroundManager
private BackgroundManager mBackgroundManager;
// Inicializamos el BackgroundManager
mBackgroundManager = BackgroundManager.getInstance(getActivity());
mBackgroundManager.attach(getActivity().getWindow());
// Cambiamos el fondo cada vez que seleccione una card
mBackgroundManager.setDrawable(backgroundDrawable);
Step 3
¿Cómo buscar dentro de la app?
//Swift
let searchResultsController = SearchResultsViewController()
let searchController = UISearchController(searchResultsController: searchResultsController)
searchController.searchResultsUpdater = searchResultsController
let searchContainer = UISearchContainerViewController(searchController: searchController)
Gestos
Veamos los styles
//Con el Appearance Proxy
UIButton.appearance().setTitleColor(.red, for: [])
let light = UITraitCollection(userInterfaceStyle: .light)
let dark = UITraitCollection(userInterfaceStyle: .dark)
UIButton.appearance(for: light).setTitleColor(.red, for:[])
UIButton.appearance(for: dark).setTitleColor(.blue, for:[])
// UIScreen, UIWindow, UIViewController, UIPresentationController, UIView
func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
Step 3
TVJS
Eventos:
Document → load, unload, appear, disappear…
Focusable Elements → select, highlight, holdselect, play
event.target vs event.currentTarget
TVML
SearchTemplate TVML
Step 3 TVML
Vayamos al detalle
Step 4
Construyendo cualquier layout
Lo más importante, la navegación
Controlando la navegación
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
// Vamos abajo
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
// Vamos arriba
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
// Vamos a la derecha
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
// Vamos a la izquiera
}
Controlando la navegación
<TextView android:id="@+id/Category1"
android:nextFocusDown="@+id/Category2">
Manejando recursos pesados
● Carga imágenes solo cuando se muestren en la pantalla.
● Recicla los Bitmaps cuando ya no sean necesarios.
● Si traes imágenes de red, hazlo siempre en un hilo aparte.
● Escala las imágenes al tamaño que realmente necesitas.
Step 4
Cargar un video
Lo más fácil es usar la clase AVPlayerViewController y el
resto sería así:
//Creamos el player con la URL del video
player = AVPlayer(url: url)
//configuramos el payer con las opciones deseadas
player?.play()
Step 4
TVML. Elementos Custom I TVML
TVML. Elementos Custom II
1.Implementar protocolo TVInterfaceCreating
2.Asignar el extendedInterfaceCreator del objeto
singleton TVInterfaceFactory
3.Registrar elemento
4.Usar en nuestros TVMLs
TVML
Custom template
Step 4 TVML
Cosas que nos dejamos
en el tintero
Finding content outside the app
● Recommendation Row:
○ Continuation content
○ New content
○ Related content
● Hacer que el contenido de tu aplicación sea encontrable
desde fuera de ella.
Más vistas reutilizables
Modo picture-in-picture
Cosas que nos dejamos en el tintero
Testing. Unit Test y UI Test
Top Shelf Dinámico
Parallax Artwork
Más Focus Engine:
∘ UIFocusGuide
∘ var preferredFocusEnvironments: [UIFocusEnvironment]
Cosas que nos dejamos en el tintero
Video features
Now playing
Animar DOM updates
Algunos enlaces
Primero pasos con AndroidTV
Building AndroidTV Apps
Guías de estilo AndroidTV
Distribución en AndroidTV
¡Gracias!

Más contenido relacionado

Similar a Tv Future is Apps - tvOS vs AndroidTV

[Globant Summer Take Over] Apple Tv Development
[Globant Summer Take Over] Apple Tv Development[Globant Summer Take Over] Apple Tv Development
[Globant Summer Take Over] Apple Tv DevelopmentGlobant
 
Construyendo una app Android sobre la nube App Engine
Construyendo una app Android sobre la nube App EngineConstruyendo una app Android sobre la nube App Engine
Construyendo una app Android sobre la nube App EngineGDG Lima
 
LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"Alberto Ruibal
 
Tema 4 3_3_interfaces_de_usuario
Tema 4 3_3_interfaces_de_usuarioTema 4 3_3_interfaces_de_usuario
Tema 4 3_3_interfaces_de_usuarioCarlos A. Iglesias
 
Como insertar un video en eclipse java
Como insertar un video en eclipse javaComo insertar un video en eclipse java
Como insertar un video en eclipse javaElizabeth Reyna
 
MobileCONGalicia Introducción a Android
MobileCONGalicia Introducción a AndroidMobileCONGalicia Introducción a Android
MobileCONGalicia Introducción a AndroidAlberto Ruibal
 
Codemotion 2016 Madrid - Dockeriza tus aplicaciones Java
Codemotion 2016 Madrid - Dockeriza tus aplicaciones JavaCodemotion 2016 Madrid - Dockeriza tus aplicaciones Java
Codemotion 2016 Madrid - Dockeriza tus aplicaciones JavaIván López Martín
 
Taller Práctico de Android
Taller Práctico de AndroidTaller Práctico de Android
Taller Práctico de AndroidJavier Muñoz
 
El Javascripto y los Emuladores de la Muerte
El Javascripto y los Emuladores de la MuerteEl Javascripto y los Emuladores de la Muerte
El Javascripto y los Emuladores de la MuerteFernando Larrañaga
 
Insertar video
Insertar videoInsertar video
Insertar video0cero
 
Programación de Apps con Android – Avanzado por Jorge Pintado
Programación de Apps con Android – Avanzado por Jorge PintadoProgramación de Apps con Android – Avanzado por Jorge Pintado
Programación de Apps con Android – Avanzado por Jorge PintadoCongresoWeb
 
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+D
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+DinLab FIB MeteorJS workshop by uLab UPC - Telefonica I+D
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+DinLabFIB
 
PhoneGap !Qué bueno que viniste¡
PhoneGap !Qué bueno que viniste¡PhoneGap !Qué bueno que viniste¡
PhoneGap !Qué bueno que viniste¡Rubén Aguilera
 
Unidad jme-02--ingbarcia-final
Unidad jme-02--ingbarcia-finalUnidad jme-02--ingbarcia-final
Unidad jme-02--ingbarcia-finalOrlando Barcia
 
Aprender a programar aplicaciones moviles
Aprender a programar aplicaciones movilesAprender a programar aplicaciones moviles
Aprender a programar aplicaciones movilesRobert Moreira
 
Desarrollar juegos para Iphone e Ipad con Cocos2D
Desarrollar juegos para Iphone e Ipad con Cocos2DDesarrollar juegos para Iphone e Ipad con Cocos2D
Desarrollar juegos para Iphone e Ipad con Cocos2Dcreagamers
 
Tech Meetup: Jenkins, the moody buttler
Tech Meetup: Jenkins, the moody buttlerTech Meetup: Jenkins, the moody buttler
Tech Meetup: Jenkins, the moody buttlerSantex Group
 

Similar a Tv Future is Apps - tvOS vs AndroidTV (20)

[Globant Summer Take Over] Apple Tv Development
[Globant Summer Take Over] Apple Tv Development[Globant Summer Take Over] Apple Tv Development
[Globant Summer Take Over] Apple Tv Development
 
Construyendo una app Android sobre la nube App Engine
Construyendo una app Android sobre la nube App EngineConstruyendo una app Android sobre la nube App Engine
Construyendo una app Android sobre la nube App Engine
 
LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"
 
Tema 4 3_3_interfaces_de_usuario
Tema 4 3_3_interfaces_de_usuarioTema 4 3_3_interfaces_de_usuario
Tema 4 3_3_interfaces_de_usuario
 
Como insertar un video en eclipse java
Como insertar un video en eclipse javaComo insertar un video en eclipse java
Como insertar un video en eclipse java
 
MobileCONGalicia Introducción a Android
MobileCONGalicia Introducción a AndroidMobileCONGalicia Introducción a Android
MobileCONGalicia Introducción a Android
 
Android
AndroidAndroid
Android
 
Codemotion 2016 Madrid - Dockeriza tus aplicaciones Java
Codemotion 2016 Madrid - Dockeriza tus aplicaciones JavaCodemotion 2016 Madrid - Dockeriza tus aplicaciones Java
Codemotion 2016 Madrid - Dockeriza tus aplicaciones Java
 
Taller Práctico de Android
Taller Práctico de AndroidTaller Práctico de Android
Taller Práctico de Android
 
El Javascripto y los Emuladores de la Muerte
El Javascripto y los Emuladores de la MuerteEl Javascripto y los Emuladores de la Muerte
El Javascripto y los Emuladores de la Muerte
 
Insertar video
Insertar videoInsertar video
Insertar video
 
Programación de Apps con Android – Avanzado por Jorge Pintado
Programación de Apps con Android – Avanzado por Jorge PintadoProgramación de Apps con Android – Avanzado por Jorge Pintado
Programación de Apps con Android – Avanzado por Jorge Pintado
 
202204-Modernizando aplicaciones legacy
202204-Modernizando aplicaciones legacy202204-Modernizando aplicaciones legacy
202204-Modernizando aplicaciones legacy
 
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+D
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+DinLab FIB MeteorJS workshop by uLab UPC - Telefonica I+D
inLab FIB MeteorJS workshop by uLab UPC - Telefonica I+D
 
PhoneGap !Qué bueno que viniste¡
PhoneGap !Qué bueno que viniste¡PhoneGap !Qué bueno que viniste¡
PhoneGap !Qué bueno que viniste¡
 
Unidad jme-02--ingbarcia-final
Unidad jme-02--ingbarcia-finalUnidad jme-02--ingbarcia-final
Unidad jme-02--ingbarcia-final
 
Aprender a programar aplicaciones moviles
Aprender a programar aplicaciones movilesAprender a programar aplicaciones moviles
Aprender a programar aplicaciones moviles
 
Desarrollar juegos para Iphone e Ipad con Cocos2D
Desarrollar juegos para Iphone e Ipad con Cocos2DDesarrollar juegos para Iphone e Ipad con Cocos2D
Desarrollar juegos para Iphone e Ipad con Cocos2D
 
Tech Meetup: Jenkins, the moody buttler
Tech Meetup: Jenkins, the moody buttlerTech Meetup: Jenkins, the moody buttler
Tech Meetup: Jenkins, the moody buttler
 
DotNetDom: El futuro de Xamarin
DotNetDom: El futuro de XamarinDotNetDom: El futuro de Xamarin
DotNetDom: El futuro de Xamarin
 

Tv Future is Apps - tvOS vs AndroidTV

  • 1. TV Future is Apps tvOS vs AndroidTV by @pablito_az @hugojperal @jr_salazares MADRID · NOV 18-19 · 2016
  • 2. Agenda ¿Quiénes somos? Introducción a la plataforma Dinámica del workshop. Primeros pasos ¿Cómo buscar contenido dentro de la app? Vayamos al detalle. Cosas que nos dejamos en el tintero
  • 8.
  • 9. Dinámica del workshop Repo AndroidTV: https://github.com/odigeoteam/AndroidTV-workshop Repo tvOS y TVML: https://github.com/odigeoteam/tvOS-workshop
  • 11. Para que sea compatible en AndroidTV ... … tenemos que hacer algunas cosas: ● Nuestras activity principal CATEGORY_LEANBACK_LAUNCHER ● <uses-feature android:name = “android.software.leanback” android:required = “false”> ● <uses-feature android:name = “android.hardware.touchscreen” android:required = “false”>
  • 12. … y nuestro dispositivo tiene limitaciones: ● Touchscreen ● Telephony ● Camera ● GPS ● Microphone ● Sensors
  • 13. Adaptando el comportamiento ... … en tiempo de ejecución: if (getPackageManager().hasSystemFeature("android.hardware.camera")) { // Hago algo con la cámara } else { // Adapto mi comportamiento }
  • 14. Algunas características de los layouts Debemos tener en cuenta: ● Layouts en Landscape. ● UI en secciones fácilmente navegables. ● Suficiente margen entre elementos. ● Cuidado con el Overscan ● Textos suficientemente visibles.
  • 15. v17 Leanback to the rescue compile 'com.android.support:leanback-v17:24.2.1'
  • 16. Nuestra primera vista: BrowseFragment
  • 17. Configurando el BrowseFragment Podemos configurar elementos en la interfaz muy fácil: setBadgeDrawable(...); setOnSearchClickedListener(...); setSearchAffordanceColor(...);
  • 19. Pintando contenido class ContentAdapter extends ArrayObjectAdapter { for (CategoryList) { ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardItemPresenter()); for (awesomePlaces) { listRowAdapter.add(places); } HeaderItem header = new HeaderItem(Category); add(new ListRow(header, listRowAdapter)); } }
  • 20. Nuestro CardItemPresenter public class CardItemPresenter extends Presenter{ @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // Creo mi ImageCardView aquí } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // Pinto la información en la ImageCardView } }
  • 21. Configurando ImageCardView Tiene 3 elementos principales, además de la imágen de fondo: Se pueden configurar, mediante un Style combinando estos elementos: <item name="lbImageCardViewType">Title|Content|IconOnLeft</item> ● Título ● Contenido ● Badge
  • 22. Step 1 - 2
  • 23. Limitaciones 200MB No LocalStorage No WebViews No Custom Video Player No Contacts, HandOff...
  • 24. Universal Purchase Misma entrada en iTunes Connect Mismo bundle ID Subidas a iTunes Connect separadas Distintas versiones Distintos procesos de revisión Esta unión es para… siempre
  • 26. Grids, grids everywhere ¿puedo tener el foco? -> canBecomeFocused UIButton, UIControl, UISegmentedControl, UITabBar, UITextField, UISearchBar(UITextField) collectionView(_:canFocusItemAtIndexPath:) tableView(_:canFocusRowAtIndexPath:) y yo, ¿tengo el foco? -> focused didUpdateFocusInContext(_:withAnimationCoordinator:)
  • 28. TVML TVML (TV Markup Language) TVJS (TV Javascript) TVMLKit TVML
  • 29. Templates, templates everywhere 18 templates a elegir Elementos simples Elementos compuestos carousel, collectionList, shelf, lockup TVML
  • 30. JS Principales application.js: JS Inicial. Carga otros JS y la primera pantalla DocumentLoader: Encargado de cargar documentos DocumentController: Es el controller y tiene asociado un loader y un template. Nos abstrae de la navegación TVML
  • 31. Aspecto template ● Para navegar entre pantallas usaremos el atributo “documentURL” en cualquier elemento focusable TVML
  • 32. Step 1 - 2 TVML
  • 33. ¿Cómo buscamos dentro de la app? Step 3
  • 35. SearchFragment class SearchViewImp extends SearchFragment implements SearchResultProvider { @Override public ObjectAdapter getResultsAdapter() { } @Override public boolean onQueryTextChange(String newQuery) { } @Override public boolean onQueryTextSubmit(String query) { } }
  • 36. Cards interaction: BackgroundManager private BackgroundManager mBackgroundManager; // Inicializamos el BackgroundManager mBackgroundManager = BackgroundManager.getInstance(getActivity()); mBackgroundManager.attach(getActivity().getWindow()); // Cambiamos el fondo cada vez que seleccione una card mBackgroundManager.setDrawable(backgroundDrawable);
  • 38. ¿Cómo buscar dentro de la app? //Swift let searchResultsController = SearchResultsViewController() let searchController = UISearchController(searchResultsController: searchResultsController) searchController.searchResultsUpdater = searchResultsController let searchContainer = UISearchContainerViewController(searchController: searchController)
  • 40. Veamos los styles //Con el Appearance Proxy UIButton.appearance().setTitleColor(.red, for: []) let light = UITraitCollection(userInterfaceStyle: .light) let dark = UITraitCollection(userInterfaceStyle: .dark) UIButton.appearance(for: light).setTitleColor(.red, for:[]) UIButton.appearance(for: dark).setTitleColor(.blue, for:[]) // UIScreen, UIWindow, UIViewController, UIPresentationController, UIView func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
  • 42. TVJS Eventos: Document → load, unload, appear, disappear… Focusable Elements → select, highlight, holdselect, play event.target vs event.currentTarget TVML
  • 46. Construyendo cualquier layout Lo más importante, la navegación
  • 47. Controlando la navegación if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { // Vamos abajo } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) { // Vamos arriba } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) { // Vamos a la derecha } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) { // Vamos a la izquiera }
  • 48. Controlando la navegación <TextView android:id="@+id/Category1" android:nextFocusDown="@+id/Category2">
  • 49. Manejando recursos pesados ● Carga imágenes solo cuando se muestren en la pantalla. ● Recicla los Bitmaps cuando ya no sean necesarios. ● Si traes imágenes de red, hazlo siempre en un hilo aparte. ● Escala las imágenes al tamaño que realmente necesitas.
  • 51. Cargar un video Lo más fácil es usar la clase AVPlayerViewController y el resto sería así: //Creamos el player con la URL del video player = AVPlayer(url: url) //configuramos el payer con las opciones deseadas player?.play()
  • 54. TVML. Elementos Custom II 1.Implementar protocolo TVInterfaceCreating 2.Asignar el extendedInterfaceCreator del objeto singleton TVInterfaceFactory 3.Registrar elemento 4.Usar en nuestros TVMLs TVML
  • 57. Cosas que nos dejamos en el tintero
  • 58. Finding content outside the app ● Recommendation Row: ○ Continuation content ○ New content ○ Related content ● Hacer que el contenido de tu aplicación sea encontrable desde fuera de ella.
  • 61. Cosas que nos dejamos en el tintero Testing. Unit Test y UI Test Top Shelf Dinámico Parallax Artwork Más Focus Engine: ∘ UIFocusGuide ∘ var preferredFocusEnvironments: [UIFocusEnvironment]
  • 62. Cosas que nos dejamos en el tintero Video features Now playing Animar DOM updates
  • 63. Algunos enlaces Primero pasos con AndroidTV Building AndroidTV Apps Guías de estilo AndroidTV Distribución en AndroidTV

Notas del editor

  1. Como suele ser habitual en estos eventos, toca hacer un poco de publicidad de la empresa. Los tres trabajamos en eDreams_Odigeo en el equipo de nativo. Jose y Hugo en la parte de iOS y yo en la parte de Android. No se si a todos os sonará la empresa, pero la actividad principal es la venta de vuelos. Además de eDreams, la empresa tiene todas las marcas que véis ahí: Opodo, GoVoyage, Liligo y Travelink. En españa tenemos sede en Barcelona y Madrid, y aunque la empresa es española tiene sedes en otros sitios fuera de españa como Londres, Paris o Budapest. Bueno y estamos contratando tanto en Madrid como Barcelona y podéis mirar todas las posciones que hay abiertas ahora mismo en el enlace que ponemos ahí. Más concretamente en el equipo de nativo también estamos contratando, tanto en Madrid como en Barcelona y si estáis interesados podéis aplicar directamente en el enlace, o en linkedin o directamente podéis escribirnos a cualquiera de los 3.
  2. La experiencia en el salón y no en la palma de la mano. Entorno.
  3. Las interacciones se hacen con el mando. Layout. El mando de AppleTV es táctil, tiene micrófono y giroscópio. No obstante no podemos suponer este método de entrada. Otros dispositivos de entrada
  4. La navegación tiene que ser clara e intuitiva. Con textos legibles a cierta distancia.
  5. En todo momento tenemos que saber que elemento tiene el foco.
  6. Se debe proporcionar una experiencia inmersiva y rica en contenido.
  7. Antes de empezar, cuántos Androides hay? y iOS Developers? y cuántos saben JS? Nuestro objetivo es que saquéis el máximo provecho de este workshop, si queréis que entremos en más detalle de algo tan sólo nos lo tenéis que decir. Dicho esto, la dinámica que vamos a seguir (a priori) va a ser la siguiente: cada uno de nosotros vamos a hacer la misma app en tres tecnologías: mi compañero Pablo explicará AndroidTV, Hugo tvOS en Swift y yo haré lo propio con TVML. Hemos dividido el proyecto en tres pasos incrementales, cada uno de ellos consta de una explicación seguida de unos breves ejercicios que podéis realizar en el tiempo que os daremos y en el cual podréis preguntarnos y consultar cualquier cosa que queráis. Los ejercicios están descritos en unos ficheros txt dentro del proyecto. Iremos intercalando las tres tecnologías así que cuando no estemos presentando será el tiempo disponible para estos ejercicios y dudas. Como he dicho, los pasos son incrementales, por lo que si os perdéis siempre podéis abrir el proyecto en el siguiente step. Android tiene su repositorio y tvOS Swift y TVML comparten el mismo repositorio y proyecto pero diferentes targets. ¿Tenéis todos el material necesario? Os parece bien? Pues venga. Comencemos
  8. Si queremos que nuestra aplicación sea reconocida a través de Google Play, nuestra activity principal tiene que especificar que vamos utilizar la interfaz leanback. Para ello hay que categorizarla como CATEGORY_LEANBACK_LAUNCHER De esta manera especificamos dos cosas: por un lado que nuestra aplicación utilizará la interfaz Leanback y por otro lado que no es obligatorio, es decir que si el dispositivo es compatible con esta interfaz la utilizará y si no, no. Si lo pusieramos a true (valor por defecto) nuestra aplicación sólo sería compatible con teles. De la misma manera que el anterior tenemos que especificar el touchscreen como opcional, sino lo hacemos, la aplicación no será compatible con teles.
  9. Si estamos adaptando una aplicación para que sea compatible con la TV, debemos marcar como opcionales todas estas funcionalidades.
  10. Se recomienda tener distintos layouts y por lo tanto distintos comportamientos para una aplicación Móvil vs TV, no obstante si aún así decidimos adaptar nuestros layouts o compartimos controladores, podemos adaptar el comportamiento en tiempo de ejecución comprobando si el dispositivo es compatible con la funcionalidad que necesitamos.
  11. Android TV apps are generally structured according to the following four modalities: Browse View: The Browse View is the usual entry point to an Android TV application. Make it easy for users to navigate your content matrix by using relevant categories to simplify decision-making. Detail View: The Detail View provides in-depth and relevant information for selected content. Place actionable content and the most relevant information in the immediately visible section of the screen, without requiring the user to scroll. Consumption View: The Consumption View is for the user to engage with or watch content. In-app Search: The In-app Search overlay provides a quick way to search for content within your app. Las interfaces de la app se presentan en orientación horizontal. (Mandatory para play store) Si dividimos la UI en secciones se facilita la navegación en horizontal y vertical mediante un mando. Si asignamos el suficiente margen entre los elementos puede distinguirse de manera más sencilla que elemento de la interfaz es el que tiene el foco. La app no exhibe texto ni funcionalidades que estén parcialmente cortados por los bordes de la pantalla. (Mandatory para playstore). La app exhibe el texto central en un tamaño de 16 sp o más grande y La app exhibe el resto del texto en un tamaño de 12 sp o más grande. (Mandatory para playstore). Evitar utilizar letras ligeras o con trazos estrechos.
  12. La librería de compatibilidad Leanback nos da algunos fragments muy útiles que podemos extender y configurar. Reutilizando las vistas que nos aporta la librería, no tenemos que preocuparnos de cumplir con las carácterísticas que hemos mencionado arriba ya que cumplen con todas las especificaciones necesarias ya que nos permite utilizar el Theme.Leanback en nuestras activities.
  13. BrowseFragment nos permite agrupar contenido por categorías ya que está compuesto de RowFragment y HeaderFragment (que veremos en el siguiente paso).
  14. Tiene métodos que nos permiten configurar muy fácil los elementos de esta vista.
  15. Para pintar contenido dentro de nuestro BrowseFragment, nos vamos apoyar en otro elemento que nos ofrece la librería v17 Leanback: el ArrayObjectAdapter que estará compuesto por los objetos que queremos pintar (las cards en este caso) agrupados en una serie de headers items que hacen la función de categorías. Nuestro BrowseFragment se encargará de agruparlos y mostrarlos sin que tengamos que hacer prácticamente nada.
  16. El comportamiento es muy similar a pintar elementos utilizando un RecyclerView por ejemplo. Tenemos nuestro adapter (ArrayContentAdapter) y tenemos nuestro ViewHolder (el elemento que nos proporciona la libreria de Leanback para esto lo llama Presenter). Como vemos aquí, solo tenemos que recorrer nuestra lista de categorías e ir creando un ArrayObjectAdapter de tipo RowAdapter (hay más tipos como SectionRow para crear solo secciones) para cada una incluyendo un CardPresenter (que veremos más adelante) por cada destino, y agrupándolos bajo un HeaderItem (categoría).
  17. Como vemos, el Presenter hace las funciones de ViewHolder. Se utiliza para generar vistas y bindear contenido en estas vistas bajo demanda. Por cada elemento que añadimos a nuestro ArrayObjectAdapter anterior (destino) vamos a crear un ImageCardView.
  18. El ImageCardView es otro de los elementos que nos ofrece la librería Leanback y nos permite montar de manera automática el elemento de las tarjetas así como la interacción que tengamos con esta. Los elementos principales son 3: título, contenido (una pequeña descripción) y el badget que es un icono que se puede mostrar en el lado izquierdo o derecho de la tarjeta.
  19. Android: BrowseView tenemos que implementar el método paintAwesomePlaces En el CardItemPresenter tenemos que configurar el tipo de card que queremos (buildDescriptionImageCardView o buildImageOnlyCardView). La que tiene descripción tiene que actualizar el background cuando tiene el foco
  20. Limitación 200MB. Se puede usar iCloud o CloudKit, NSUserDefaults, resources on demand. NSCachesDirectory NSTemporaryDirectory se pueden escribir. Solo 500KB? #if os(tvOS)?
  21. En el mismo proyecto puedes tener un target para la app de ios y otro para la de tvos. A parte puedes compartir código por frameworks. Cuando empiece el step se puede explicar como se estructura el proyecto
  22. En este paso añadiremos el botón izquierdo al navigation bar. Nos damos cuenta que el botón sigue las guías de diseño de Apple. Y que los elementos cuando tienen el foco suelen hacerse más grandes.
  23. Lo más importante de un grid es saber quien tiene el foco. Saber si una vista puede tener el foco es importante, incluso si lo tiene. Pero también nos ayuda el didUpdateFocusInContext(_:withAnimationCoordinator:) para participar en la actualización del foco y en las animaciones asociadas. También podemos pedir una actualización del foco con setNeedsFocusUpdate() y updateFocusIfNeeded()
  24. TVML es un formato especial de XML con etiquetas específicas definicas por Apple. Con este XML podemos definir las vistas TVJS es Javascript con alguna peculiaridades específicas del AppleTV. Este será nuestro controllador. Desde la versión 10 se puede usar el estándar ECMAScript 6. Por último TVMLKit es el puente entre la parte servidor y la app en sí. ¿Qué pinta tienen las apps hechas en TVML? Por una parte tendremos la app instalada en el Apple TV que se comunicará con el servidor que aloja los ficheros JS y TVML. Normalmente los ficheros javascript serán los encargados de cargar las plantillas TVMLs, modificarlas y hacer la navegación. ¿Por qué usar TVML? Existen varias razones: Todas las apps del AppStore realizadas por Apple están hechas en TVML. Con muy poco esfuerzo podemos tener una app vistosa y acorde a la guía de estilos de Apple. El resultado es una app nativa, no es un webview. TVMLKit se encarga de traducir los elementos TVML a elementos nativos. Podemos actualizar la app sin tener que crear una nueva versión y al tener todos los elementos en el servidor, nuestra app ocupará muy poco Por último, en el caso de que lo necesitemos, podemos invocar código Swift desde JS (por ejemplo, para usar un sdk de analytics), y también a la inversa, desde Swift a JS.
  25. Dejar claro que la parte servidor no tiene nada que ver con el proyecto
  26. Como ya somos expertos en TVML vamos a modificar el template que tenemos para el listado. Vamos a añadir una cabecera que contenga lo siguiente: * A la izquierda un botón con el icono de la lupa ("search.png") * En el centro un texto * A la derecha una imagen, que será nuestro logo ("logo.png")
  27. Al igual que con el BrowseFragment, la librería de Leanback también nos facilita la vida a la hora de buscar contenido dentro de nuestra aplicación con otro Fragment nativo: searchFragment.
  28. Para poder utilizarlo, solamente tenemos que crear un Fragment que extienda de SearchFragment e implementar SearchResultProvider que nos ayudará a la hora de pintar los resultados. El adapter es exactamente el mismo que el que utilizábamos en el BrowseView y los resultados se pintarán exactamente igual. Si no queremos que la búsqueda se lance cada vez que el usuario introduce un nuevo carácter, es conveniente retrasarla algunos milisegundos (300 sueles ser suficiente).
  29. Vamos a interactuar con las tarjetas cambiando el fondo de la pantalla cada vez que una tarjeta es seleccionada, tanto el Browse como en el Search. Para eso vamos a utilizar el BackgroundManager que nos permite mantener la misma imágen de Background entre distintas activites. Al igual que las search, no queremos que se actualice si el usuario pasa muy rápido por las tarjetas, por lo que vamos a meterle un retardo también de 300ms
  30. Android: Para la parte de la búsqueda, implementar los métodos getResultAdapter y doSearch. No es necesario pintar de nuevo las cards porque es exactamente igual en el paso anterior Para la parte del background: implementar setUpUIElements, prepareBackgroundManager y updateBackground tanto en SearchView como en BrowseView
  31. El result controller normalmente tiene un grid que se encarga de mostrar los resultados. Se crea el search controller. Las actualizaciones del search las suele manejar el result. Ponemos el search controller en el container y lo presentamos.
  32. Podemos crearnos distintos gestures recognizers. Para dar compatibilidad a distintos mandos o cambiar comportamiento a los botones como el menu, imaginaros en un juego darle a menu y salirnos de la partida.
  33. target es quien disparó el evento. currentTarget es quien tenía asignado el evento.
  34. modificar el DOM. Depurar
  35. En este paso programaremos un poco de JS. El objetivo de este step es setear el título del header del List.xml con el título del elemento que tenga el foco. Para ello escucharemos al evento "highlight" del template. También queremos que cuando el usuario pulse el botón de play estando en el listado, le aparezca la pantalla de búsqueda. El evento en ese caso es... "play" :)
  36. Hasta ahora, nos hemos basados en componentes heredados de la librería de compatibilidad Leanback pero, ¿qué pasa si queremos construir nuestra propia vista? A través de la pantalla de detalle, veremos cómo es posible construir cualquier tipo de layout y cómo el framework nos puede facilitar la navegación a través de los distintos elementos. Para mi es el aspecto más importante: como hemos repetido durante todo el workshop, una navegación clara entre los distintos elementos.
  37. Hay dos formas principales de controlar la navegación a través de los distintos elementos de la pantalla. La primera es esta: podemos setear el OnKeyListener y hacer lo que nosotros queramos dependiendo de la dirección hacia la que el usuario quiera navegar.
  38. Como forma alternativa a lo anterior, el framework de Android ya provee un sistema de navegación direccional entre los elementos basándose en la posición relativa de los elementos focusables que además se puede modificar tal y como se ve en la transparencia: disponemos de nextFocusDown, nextFocusUp, nextFocusRight y nextFocusLeft.
  39. Otro de los problemas importantes a los que nos tendremos que enfrentar es que puesto que la experiencia ha de ser lo más cinematográfica y envolvente posible, los más probable es que tengamos que manejar videos e imágenes en altas resoluciones en un dispositivo con un sistema limitado de memoria. Para evitar out of memory errors, debemos tener en cuenta algunas consideraciones:
  40. Android: Debemos de setear el onKeyListener para que llame al presenter El presenter tiene que decidir que acción ejecutar dependiendo de la tecla que hayamos pulsado.
  41. En este step cargaremos el controlador de detalle, pondremos el video y veremos una breve descripción del viaje.
  42. Ejercicio: Poder usar de la lupa como resource
  43. Para finalizar nuestro querido workshop vamos a hacer tres pequeñas tareas. 1 -. En el listado, en vez de usar la imagen de la lupa descargada de internet, queremos usar la que tenemos dentro del proyecto. Para ello tendrás que implementar uno de los métodos del protocolo TVInterfaceCreating. Swift → JS Podemos obtener el contexto JS llamando al método evaluateInJavaScriptContext de la instancia TVApplicationController que se crea en el AppDelegate. Una vez tenemos el contexto lo único que tenemos que hacer es pasarle un evento mediante el método invokeMethod 2 -. Queremos enviar un evento desde Swift a TVJS, concretamente cuando detectemos que la app viene de background a foreground (applicationWillEnterForeground...) Para enviar el mensaje usaremos el método "executeRemoteMethod" que está en el AppDelegate. El javascript que reciba el evento, mostrará una alerta con un mensaje. JS → Swift 3 -. Para ello tenemos que crear un protocolo en objc que implemente el protocol JSExport, ahí dentro pondremos el método que queramos (le podemos poner parámetros). A continuación crearemos una clase que extienda de NSObject y que implemente el protocolo que creamos antes. Lo último que nos queda es crear una instancia de nuestro objeto y setearlo en el contexto de JS, para ello necesitaremos implementar el método appController(appController, evaluateAppJavaScriptInjsContext) Haciendo esto tendremos siempre disponible en el JS la instancia del objeto bajo el nombre que le hemos dado en el último método. Lo que queda es llamar al método cuando queramos, por ejemplo cuando cambiamos el foco en el listado. BONUS-. Crea tu propio template! Tomando como referencia el template que hemos creado, haz lo propio pero esta vez para la búsqueda. No olvides registrar la vista en el TVElementFactory!
  44. Content recommendations appear as the first row of the TV home screen after the first use of the device. Contributing recommendations from your app's content catalog can help bring users back to your app. Las recomendaciones se crean mediante procesos en background que de vez en cuando añaden el catálogo de tu aplicación al sistema de recomendaciones.
  45. Embeber vídeos directamente en el TVML que se ejecutan cuando tienen el foco.