Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Training Program - Portugal, Aula 6

ATP Portugal
November 18, 2020

Android Training Program - Portugal, Aula 6

Aula #6: Jetpack, Jetpack, Jetpack! 🚀

Ao longo das aulas temos vindo a utilizar algumas das bibliotecas do Android Jetpack, nesta aula vamos focar-nos essencialmente em três:
- Room
- CameraX
- Introdução a Compose

ATP Portugal

November 18, 2020
Tweet

More Decks by ATP Portugal

Other Decks in Education

Transcript

  1. Android
    Training
    Program
    PORTUGAL
    Aula #6
    Jetpack, jetpack, jetpack

    View full-size slide

  2. ● Sejam excelentes uns para os outros
    ● Fale mais alto se vir ou ouvir alguma coisa
    ● O assédio não é tolerado
    ● Pratique "Sim e" um ao outro
    Código de conduta
    Mais informações: http://bit.ly/2IhF0l3

    View full-size slide

  3. Andres-Leonardo
    Martinez-Ortiz
    Google
    Carlos Mota
    Formador
    Renato Almeida
    Formador
    @davilagrau @cafonsomota @tallnato
    Equipa
    Daniela Ferreira
    Gestora de
    comunidades

    View full-size slide

  4. ● 12 aulas
    ● 1h30 cada aula
    ● ~1 aula por semana
    ● 14 Outubro a 16 Dezembro
    ● YouTube live
    ● Suporte assíncrono contínuo via Discord/email
    ● Todo o código disponível no GitHub
    Photo by Arif Riyanto on Unspla
    O programa

    View full-size slide

  5. #0
    14 de Outubro
    Pronto para
    começar
    #1
    21 de Outubro
    Bem-vindos ao
    Android
    #2
    28 de Outubro
    Fundações I
    #3
    04 de Novembro
    Fundações II
    #4
    11 de Novembro
    Fundações III
    #5
    18 de Novembro
    Listas, listas e
    mais listas
    #6
    25 de Novembro
    Jetpack,
    Jetpack,
    Jetpack!
    #7 - #8
    02 - 03 de Dezembro
    Firebase
    #9 - #10
    09 - 10 de Dezembro
    MLKit &
    TensorFlow
    #11
    16 de Dezembro
    Resumo
    Semana Semana
    Calendário
    ✅ ✅ ✅ ✅
    Direto


    View full-size slide

  6. Sumário
    Photo by Mika Baumeister on Unsplash
    ● Resumo da aula anterior
    ● Jetpack
    ● Room
    ● CameraX
    ● Compose
    ● Kotlin para principiantes
    ● Sexta-Feira negra

    View full-size slide

  7. http://events.withgoogle.com/atp2020
    [email protected]
    http://bit.ly/atp2020-youtube
    http://bit.ly/atp2020-discord
    Links

    View full-size slide

  8. http://bit.ly/atp2020-live

    View full-size slide

  9. http://bit.ly/atp2020-codelabs

    View full-size slide

  10. http://bit.ly/kahoot-aula6

    View full-size slide

  11. Resumo da Aula #5

    View full-size slide

  12. ● É a evolução da ListView
    ○ Com uma maior performance e flexibilidade
    ● Permite criar uma lista de objetos facilmente
    ● Esta lista tanto pode ser horizontal como vertical
    ○ Dependendo do LayoutManager definido
    ● É possibilidade adicionar animações (incríveis) por cada item modificado
    RecyclerView

    View full-size slide

  13. RecyclerView
    Activity
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Adapter
    ViewHolder
    data

    View full-size slide

  14. ● Cliente REST fortemente tipado
    ● Facilita a transferência de JSON através de um webservice
    ○ JSON ou outro tipos de dados
    Retrofit
    https://github.com/square/retrofit

    View full-size slide

  15. Agora mais bonito ✨
    {
    "bred_for": "Small rodent hunting,
    lapdog",
    "breed_group": "Toy",
    "height": {...},
    "id": 1,
    "life_span": "10 - 12 years",
    "name": "Affenpinscher",
    "origin": "Germany, France",
    "temperament": "Stubborn, Curious,
    Playful, Adventurous, Active,
    Fun-loving",
    "weight": {...}
    },
    ...
    data class Dog(
    @Json(name = "bred_for")
    val bredFor: String,
    @Json(name = "breed_group")
    val breedGroup: String,
    val height: Height,
    val id: Int,
    @Json(name = "life_span")
    val lifeSpan: String,
    val name: String,
    val origin: String,
    val temperament: String,
    val weight: Weight
    )

    View full-size slide

  16. ● Biblioteca de conversão de JSON para Kotlin e Java
    ● Optimizada para Android
    ● Compatível Retrofit
    Moshi

    View full-size slide

  17. ● Glide é uma biblioteca de carregamento de imagens para Android
    ● Descarrega, descodifica e mostra
    ○ Imagens
    ○ GIF’s
    ● Permite também redimensionar imagens
    ● Cache automática e simplificada das imagens
    ● Expõe uma API flexível e simples de utilizar
    Vantagens
    https://github.com/bumptech/glide

    View full-size slide

  18. https://developer.android.com/jetpack

    View full-size slide

  19. Seguir boas práticas
    Construídas com base em boas práticas de design modernas, as bibliotecas do Android
    Jetpack permitem menos crashes e memory leaks, com compatibilidade com versões
    anteriores já incluídas
    Porquê usar Android Jetpack?

    View full-size slide

  20. Seguir boas práticas
    Construídas com base em boas práticas de design modernas, as bibliotecas do Android
    Jetpack permitem menos crashes e memory leaks, com compatibilidade com versões
    anteriores já incluídas
    Porquê usar Android Jetpack?
    Eliminar código boilerplate
    O Android Jetpack gere aquelas atividades chatas, como processamento em background,
    navegação e gestão do ciclo de vida da aplicação, para que os programadores se possam
    concentrar no que torna a aplicação espetacular.

    View full-size slide

  21. Seguir boas práticas
    Construídas com base em boas práticas de design modernas, as bibliotecas do Android
    Jetpack permitem menos crashes e memory leaks, com compatibilidade com versões
    anteriores já incluídas
    Porquê usar Android Jetpack?
    Eliminar código boilerplate
    O Android Jetpack gere aquelas atividades chatas, como processamento em background,
    navegação e gestão do ciclo de vida da aplicação, para que os programadores se possam
    concentrar no que torna a aplicação espetacular.
    Reduzir a fragmentação
    Reduzir a complexidade através de bibliotecas que funcionam de forma consistente nas
    várias versões e dispositivos de Android.

    View full-size slide

  22. Menos código == Menos bugs
    - Alguém

    View full-size slide

  23. ViewModel
    Paging 3
    LiveData

    View full-size slide

  24. LiveData
    ViewModel
    Paging 3
    Compose
    Room
    Camera X

    View full-size slide

  25. Testemunhos
    ‍⚖

    View full-size slide

  26. “Com os Componentes de Arquitetura do Android, estamos a re-arquitetar toda a
    nossa aplicação. É ótimo existir uma forma recomenda, opinativa, e clara de
    construir uma aplicação Android que facilita suportar alteração de
    configurações”
    - Drew Hannay, Engenheiro de Software, LinkedIn
    https://developer.android.com/jetpack/testimonials
    LinkedIn
    Testemunhos

    View full-size slide

  27. “Nós utilizamos dezenas de diferentes tecnologias, e o ‘Room’ foi uma grande
    melhoria. A ênfase na testabilidade é enorme.
    - Andy Lawton, responsável pela plataforma Android no Tinder
    https://developer.android.com/jetpack/testimonials
    Tinder
    Testemunhos

    View full-size slide

  28. ● Camada de abstração sobre SQLite (base de dados)
    ● Reduz a quantidade de código repetitivo
    ● Acesso à base de dados de forma mais robusta
    ● Valida as consultas em tempo de compilação
    ● Utilizado como cache da aplicação
    ● Compatível com LiveData
    Room
    https://developer.android.com/topic/libraries/architecture/room

    View full-size slide

  29. Como utilizar?
    Importar a biblioteca
    Room
    Resto da aplicação
    Data Access Object
    Entidades
    Obter os DAOs
    Obter as entidades da BD
    Persistir os dados para a BD
    get / set valores

    View full-size slide

  30. plugins {

    id 'kotlin-kapt'
    }
    dependencies {
    ...
    def room_version = "2.2.5"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    // opcional - Kotlin Extensions e suporte para Coroutines
    implementation "androidx.room:room-ktx:$room_version"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View full-size slide

  31. @Entity(tableName = "Dog")
    class DogModel(
    @PrimaryKey(autoGenerate = false)
    var id: Int,
    @ColumnInfo(name = "bredFor")
    val bredFor: String,
    @ColumnInfo(name = "breedGroup")
    val breedGroup: String,
    @ColumnInfo(name = "lifeSpan")
    val lifeSpan: String,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "origin")
    val origin: String,
    @ColumnInfo(name = "temperament")
    val temperament: String
    )
    Como utilizar?
    Definir a entidade
    DogModel.kt

    View full-size slide

  32. @Entity(tableName = "Dog")
    class DogModel(
    @PrimaryKey(autoGenerate = false)
    var id: Int,
    @ColumnInfo(name = "bredFor")
    val bredFor: String,
    @ColumnInfo(name = "breedGroup")
    val breedGroup: String,
    @ColumnInfo(name = "lifeSpan")
    val lifeSpan: String,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "origin")
    val origin: String,
    @ColumnInfo(name = "temperament")
    val temperament: String
    )
    Como utilizar?
    Definir a entidade
    DogModel.kt

    View full-size slide

  33. @Entity(tableName = "Dog")
    class DogModel(
    @PrimaryKey(autoGenerate = false)
    var id: Int,
    @ColumnInfo(name = "bredFor")
    val bredFor: String,
    @ColumnInfo(name = "breedGroup")
    val breedGroup: String,
    @ColumnInfo(name = "lifeSpan")
    val lifeSpan: String,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "origin")
    val origin: String,
    @ColumnInfo(name = "temperament")
    val temperament: String
    )
    Como utilizar?
    Definir a entidade
    DogModel.kt

    View full-size slide

  34. @Entity(tableName = "Dog")
    class DogModel(
    @PrimaryKey(autoGenerate = false)
    var id: Int,
    @ColumnInfo(name = "bredFor")
    val bredFor: String,
    @ColumnInfo(name = "breedGroup")
    val breedGroup: String,
    @ColumnInfo(name = "lifeSpan")
    val lifeSpan: String,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "origin")
    val origin: String,
    @ColumnInfo(name = "temperament")
    val temperament: String
    )
    Como utilizar?
    Definir a entidade
    DogModel.kt

    View full-size slide

  35. @Dao
    interface DogDAO {
    @Query("SELECT * FROM dog ORDER BY name ASC")
    fun getAlphabetizedDogs(): LiveData>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dog: Dog)
    @Query("DELETE FROM dog")
    fun deleteAll()
    }
    Como utilizar?
    Definir o DAO (Data access object)
    DogDAO.kt

    View full-size slide

  36. @Dao
    interface DogDAO {
    @Query("SELECT * FROM dog ORDER BY name ASC")
    fun getAlphabetizedDogs(): LiveData>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dog: DogModel)
    @Query("DELETE FROM dog")
    fun deleteAll()
    }
    Como utilizar?
    Definir o DAO (Data access object)
    DogDAO.kt

    View full-size slide

  37. @Dao
    interface DogDAO {
    @Query("SELECT * FROM dog ORDER BY name ASC")
    fun getAlphabetizedDogs(): LiveData>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dog: DogModel)
    @Query("DELETE FROM dog")
    fun deleteAll()
    }
    Como utilizar?
    Definir o DAO (Data access object)
    DogDAO.kt

    View full-size slide

  38. @Dao
    interface DogDAO {
    @Query("SELECT * FROM dog ORDER BY name ASC")
    fun getAlphabetizedDogs(): LiveData>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dog: DogModel)
    @Query("DELETE FROM dog")
    fun deleteAll()
    }
    Como utilizar?
    Definir o DAO (Data access object)
    DogDAO.kt

    View full-size slide

  39. @Dao
    interface DogDAO {
    @Query("SELECT * FROM dog ORDER BY name ASC")
    fun getAlphabetizedDogs(): LiveData>
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(dog: Dog)
    @Query("DELETE FROM dog")
    fun deleteAll()
    }
    Como utilizar?
    Definir o DAO (Data access object)
    DogDAO.kt

    View full-size slide

  40. @Database(entities = [DogModel::class], version = 1, exportSchema = false)
    abstract class KennelDatabase : RoomDatabase() {
    abstract fun dogDao(): DogDAO
    companion object {
    @Volatile
    private var INSTANCE: KennelDatabase? = null
    fun getDatabase(context: Context): KennelDatabase {
    return INSTANCE ?: synchronized(this) {
    val instance = Room.databaseBuilder(
    context.applicationContext,KennelDatabase::class.java, "dog_database"
    ).build()
    INSTANCE = instance
    instance
    }
    }
    val databaseWriteExecutor = Executors.newFixedThreadPool(2)
    }
    }
    Como utilizar?
    A base de dados
    KennelDatabase.kt

    View full-size slide

  41. @Database(entities = [DogModel::class], version = 1, exportSchema = false)
    abstract class KennelDatabase : RoomDatabase() {
    abstract fun dogDao(): DogDAO
    companion object {
    @Volatile
    private var INSTANCE: KennelDatabase? = null
    fun getDatabase(context: Context): KennelDatabase {
    return INSTANCE ?: synchronized(this) {
    val instance = Room.databaseBuilder(
    context.applicationContext,KennelDatabase::class.java, "dog_database"
    ).build()
    INSTANCE = instance
    instance
    }
    }
    val databaseWriteExecutor = Executors.newFixedThreadPool(2)
    }
    }
    Como utilizar?
    A base de dados
    KennelDatabase.kt

    View full-size slide

  42. @Database(entities = [DogModel::class], version = 1, exportSchema = false)
    abstract class KennelDatabase : RoomDatabase() {
    abstract fun dogDao(): DogDAO
    companion object {
    @Volatile
    private var INSTANCE: KennelDatabase? = null
    fun getDatabase(context: Context): KennelDatabase {
    return INSTANCE ?: synchronized(this) {
    val instance = Room.databaseBuilder(
    context.applicationContext,KennelDatabase::class.java, "dog_database"
    ).build()
    INSTANCE = instance
    instance
    }
    }
    val databaseWriteExecutor = Executors.newFixedThreadPool(2)
    }
    }
    Como utilizar?
    A base de dados
    KennelDatabase.kt

    View full-size slide

  43. @Database(entities = [DogModel::class], version = 1, exportSchema = false)
    abstract class KennelDatabase : RoomDatabase() {
    abstract fun dogDao(): DogDAO
    companion object {
    @Volatile
    private var INSTANCE: KennelDatabase? = null
    fun getDatabase(context: Context): KennelDatabase {
    return INSTANCE ?: synchronized(this) {
    val instance = Room.databaseBuilder(
    context.applicationContext,KennelDatabase::class.java, "dog_database"
    ).build()
    INSTANCE = instance
    instance
    }
    }
    val databaseWriteExecutor = Executors.newFixedThreadPool(2)
    }
    }
    Como utilizar?
    A base de dados
    KennelDatabase.kt

    View full-size slide

  44. @Database(entities = [DogModel::class], version = 1, exportSchema = false)
    abstract class KennelDatabase : RoomDatabase() {
    abstract fun dogDao(): DogDAO
    companion object {
    @Volatile
    private var INSTANCE: KennelDatabase? = null
    fun getDatabase(context: Context): KennelDatabase {
    return INSTANCE ?: synchronized(this) {
    val instance = Room.databaseBuilder(
    context.applicationContext,KennelDatabase::class.java, "dog_database"
    ).build()
    INSTANCE = instance
    instance
    }
    }
    val databaseWriteExecutor = Executors.newFixedThreadPool(2)
    }
    }
    Como utilizar?
    A base de dados
    KennelDatabase.kt

    View full-size slide

  45. class DogRepository(private val dogDao: DogDAO) {
    val allDogs: LiveData> = dogDao.getAlphabetizedDogs()
    fun insert(dog: DogModel) {
    KennelDatabase.databaseWriteExecutor
    .execute {
    dogDao.insert(dog)
    }
    }
    }
    Como utilizar?
    Repositório
    DogRepository.kt

    View full-size slide

  46. class DogRepository(private val dogDao: DogDAO) {
    val allDogs: LiveData> = dogDao.getAlphabetizedDogs()
    fun insert(dog: DogModel) {
    KennelDatabase.databaseWriteExecutor
    .execute {
    dogDao.insert(dog)
    }
    }
    }
    Como utilizar?
    Repositório
    DogRepository.kt

    View full-size slide

  47. class DogRepository(private val dogDao: DogDAO) {
    val allDogs: LiveData> = dogDao.getAlphabetizedDogs()
    fun insert(dog: DogModel) {
    KennelDatabase.databaseWriteExecutor
    .execute {
    dogDao.insert(dog)
    }
    }
    }
    Como utilizar?
    Repositório
    DogRepository.kt

    View full-size slide

  48. class MainViewModel(
    private val repository: DogRepository
    ) : ViewModel() {
    ...
    fun getDogsFromDatabase(): LiveData> {
    return repository.allDogs
    }
    fun addDog(dog : Dog) {
    repository.insert(toDogModel(dog))
    }
    }
    Como utilizar?
    No ViewModel
    MainViewModel.kt

    View full-size slide

  49. class MainViewModel(
    private val repository: DogRepository
    ) : ViewModel() {
    ...
    fun getDogsFromDatabase(): LiveData> {
    return repository.allDogs
    }
    fun addDog(dog : Dog) {
    repository.insert(toDogModel(dog))
    }
    }
    Como utilizar?
    No ViewModel
    MainViewModel.kt

    View full-size slide

  50. class MainViewModel(
    private val repository: DogRepository
    ) : ViewModel() {
    ...
    fun getDogsFromDatabase(): LiveData> {
    return repository.allDogs
    }
    fun addDog(dog : Dog) {
    repository.insert(toDogModel(dog))
    }
    }
    Como utilizar?
    No ViewModel
    MainViewModel.kt

    View full-size slide

  51. class MainViewModelFactory(
    private val repository: DogRepository
    ) : ViewModelProvider.Factory {
    override fun create(modelClass: Class): T {
    if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
    @Suppress("UNCHECKED_CAST")
    return MainViewModel(repository) as T
    }
    throw IllegalArgumentException("Unknown ViewModel class")
    }
    }
    Como utilizar?
    No ViewModelProvider
    MainViewModel.kt

    View full-size slide

  52. class BobiApplication: Application() {
    val database by lazy { KennelDatabase.getDatabase(this) }
    val repository by lazy { DogRepository(database.dogDao()) }
    }
    Como utilizar?
    O Application
    BobiApplication.kt

    View full-size slide


  53. ...
    android:name=".BobiApplication"
    ...>
    ...


    Como utilizar?
    O Application no AndroidManifest
    AndroidManifest.xml

    View full-size slide

  54. class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
    ...
    }
    Como utilizar?
    Na Activity
    MainActivity.kt

    View full-size slide

  55. class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels {
    MainViewModelFactory((application as BobiApplication).repository)
    }
    ...
    }
    Como utilizar?
    Na Activity
    MainActivity.kt

    View full-size slide

  56. class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels {
    MainViewModelFactory((application as BobiApplication).repository)
    }
    fun showDogs() {
    viewModel.getDogsDatabase().observe(this){
    Log.d(TAG, "$it")
    }
    }
    ...
    }
    Como utilizar?
    Na Activity
    MainActivity.kt

    View full-size slide

  57. CameraXβ
    Sistema de add-ons
    Consistência em todos os
    diapositivos
    Funciona com 94% dos
    Android 5.0+

    View full-size slide

  58. CameraXβ
    Captura
    Análise de imagem
    Pré-visualização
    Casos de uso
    https://developer.android.com/training/camerax

    View full-size slide

  59. Em vez de chamarmos a câmera nativa
    Câmera
    private fun openNativeCamera() {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    }

    View full-size slide

  60. Em vez de chamarmos a câmera nativa
    Câmera
    private fun openNativeCamera() {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    }
    Passamos a criar tudo de raiz...

    View full-size slide

  61. Câmera
    package pt.atp.bobi.presentation.ui
    private const val TAG = "CameraActivity"
    private const val REQUEST_CAMERA = 0
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    class CameraActivity : AppCompatActivity() {
    private lateinit var imageCapture: ImageCapture
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    if (allPermissionsGranted()) {
    startCamera()
    } else {
    ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CAMERA)
    }
    findViewById(R.id.btn_camera).setOnClickListener { takePhoto() }
    }
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
    ContextCompat.checkSelfPermission(
    baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
    private fun getOutputDirectory(): File {
    val mediaDir = externalMediaDirs.firstOrNull()?.let {
    File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
    return if (mediaDir != null && mediaDir.exists())
    mediaDir else filesDir
    }
    private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val viewFinder = findViewById(R.id.viewFinder)
    val preview = Preview.Builder()
    .build()
    .also {
    it.setSurfaceProvider(viewFinder.surfaceProvider)
    }
    val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    imageCapture = ImageCapture.Builder()
    .setTargetRotation(viewFinder.display.rotation)
    .build()
    try {
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, cameraSelector, preview, imageCapture)
    } catch (e: Exception) {
    Log.e(TAG, "Use case binding failed. Error:$e")
    }
    }, ContextCompat.getMainExecutor(this))
    }
    private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    val savedUri = Uri.fromFile(photoFile)
    val msg = "Photo capture succeeded: $savedUri"
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
    Log.d(TAG, msg)
    }
    override fun onError(exception: ImageCaptureException) {
    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
    }
    })
    }
    }

    View full-size slide

  62. Câmera
    package pt.atp.bobi.presentation.ui
    private const val TAG = "CameraActivity"
    private const val REQUEST_CAMERA = 0
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    class CameraActivity : AppCompatActivity() {
    private lateinit var imageCapture: ImageCapture
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    if (allPermissionsGranted()) {
    startCamera()
    } else {
    ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CAMERA)
    }
    findViewById(R.id.btn_camera).setOnClickListener { takePhoto() }
    }
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
    ContextCompat.checkSelfPermission(
    baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
    private fun getOutputDirectory(): File {
    val mediaDir = externalMediaDirs.firstOrNull()?.let {
    File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
    return if (mediaDir != null && mediaDir.exists())
    mediaDir else filesDir
    }
    private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val viewFinder = findViewById(R.id.viewFinder)
    val preview = Preview.Builder()
    .build()
    .also {
    it.setSurfaceProvider(viewFinder.surfaceProvider)
    }
    val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    imageCapture = ImageCapture.Builder()
    .setTargetRotation(viewFinder.display.rotation)
    .build()
    try {
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, cameraSelector, preview, imageCapture)
    } catch (e: Exception) {
    Log.e(TAG, "Use case binding failed. Error:$e")
    }
    }, ContextCompat.getMainExecutor(this))
    }
    private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    val savedUri = Uri.fromFile(photoFile)
    val msg = "Photo capture succeeded: $savedUri"
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
    Log.d(TAG, msg)
    }
    override fun onError(exception: ImageCaptureException) {
    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
    }
    })
    }
    }
    ‍♀

    View full-size slide

  63. Câmera
    package pt.atp.bobi.presentation.ui
    private const val TAG = "CameraActivity"
    private const val REQUEST_CAMERA = 0
    private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    class CameraActivity : AppCompatActivity() {
    private lateinit var imageCapture: ImageCapture
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    if (allPermissionsGranted()) {
    startCamera()
    } else {
    ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CAMERA)
    }
    findViewById(R.id.btn_camera).setOnClickListener { takePhoto() }
    }
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
    ContextCompat.checkSelfPermission(
    baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
    private fun getOutputDirectory(): File {
    val mediaDir = externalMediaDirs.firstOrNull()?.let {
    File(it, resources.getString(R.string.app_name)).apply { mkdirs() } }
    return if (mediaDir != null && mediaDir.exists())
    mediaDir else filesDir
    }
    private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val viewFinder = findViewById(R.id.viewFinder)
    val preview = Preview.Builder()
    .build()
    .also {
    it.setSurfaceProvider(viewFinder.surfaceProvider)
    }
    val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    imageCapture = ImageCapture.Builder()
    .setTargetRotation(viewFinder.display.rotation)
    .build()
    try {
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, cameraSelector, preview, imageCapture)
    } catch (e: Exception) {
    Log.e(TAG, "Use case binding failed. Error:$e")
    }
    }, ContextCompat.getMainExecutor(this))
    }
    private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    val savedUri = Uri.fromFile(photoFile)
    val msg = "Photo capture succeeded: $savedUri"
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
    Log.d(TAG, msg)
    }
    override fun onError(exception: ImageCaptureException) {
    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
    }
    })
    }
    }
    ● Fonte: 2pts
    ● 118 linhas de código
    ● Mostra o que a câmera está a gravar
    ● Permite tirar fotos

    View full-size slide

  64. plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    }
    ...
    dependencies {
    ...
    implementation "androidx.camera:camera-camera2:1.0.0-beta12"
    implementation "androidx.camera:camera-lifecycle:1.0.0-beta12"
    implementation "androidx.camera:camera-view:1.0.0-alpha19"
    }
    Como utilizar?
    Importar as bibliotecas
    app/build.gradle

    View full-size slide

  65. class CameraActivity : AppCompatActivity() {
    ...
    }

    Como utilizar?
    Criar uma nova Activity
    AndroidManifest.xml
    CameraActivity.kt

    View full-size slide



  66. Como utilizar?
    Declarar a permissão de câmera
    AndroidManifest.xml

    View full-size slide



  67. Como utilizar?
    Declarar a permissão de câmera
    AndroidManifest.xml

    View full-size slide

  68. override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    if (ContextCompat.checkSelfPermission(
    baseContext,
    Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    startCamera()
    } else {
    ActivityCompat.requestPermissions(
    this,
    arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA)
    }
    }
    Como utilizar?
    Pedir a permissão de câmera
    CameraActivity.kt

    View full-size slide

  69. override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera)
    if (ContextCompat.checkSelfPermission(
    baseContext,
    Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    startCamera()
    } else {
    ActivityCompat.requestPermissions(
    this,
    arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA)
    }
    }
    Como utilizar?
    Pedir a permissão de câmera
    CameraActivity.kt

    View full-size slide

  70. override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array,
    grantResults: IntArray) {
    if (requestCode == REQUEST_CAMERA) {
    if (ContextCompat.checkSelfPermission(
    baseContext,
    Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    startCamera()
    } else {
    Toast.makeText(this, "Permission not granted.", Toast.LENGTH_SHORT).show()
    }
    }
    Como utilizar?
    Pedir a permissão de câmera
    CameraActivity.kt

    View full-size slide

  71. override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array,
    grantResults: IntArray) {
    if (requestCode == REQUEST_CAMERA) {
    if (ContextCompat.checkSelfPermission(
    baseContext,
    Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    startCamera()
    } else {
    Toast.makeText(this, "Permission not granted.", Toast.LENGTH_SHORT).show()
    }
    }
    Como utilizar?
    Pedir a permissão de câmera
    CameraActivity.kt

    View full-size slide


  72. ...
    android:id="@+id/camera_capture_button"
    ...
    android:id="@+id/viewFinder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    Como utilizar?
    Definir o layout
    activity_camera.kt

    View full-size slide


  73. ...
    android:id="@+id/camera_capture_button"
    ...
    android:id="@+id/viewFinder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    Como utilizar?
    Definir o layout
    activity_camera.kt

    View full-size slide

  74. private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val preview = Preview.Builder().build().also {
    it.setSurfaceProvider(findViewById(R.id.viewFinder).surfaceProvider)
    }
    imageCapture = ImageCapture.Builder().build()
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, DEFAULT_BACK_CAMERA, preview, imageCapture)
    }, ContextCompat.getMainExecutor(this))
    }
    Como utilizar?
    Inicializar a câmera
    CameraActivity.kt

    View full-size slide

  75. private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val preview = Preview.Builder().build().also {
    it.setSurfaceProvider(findViewById(R.id.viewFinder).surfaceProvider)
    }
    imageCapture = ImageCapture.Builder().build()
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, DEFAULT_BACK_CAMERA, preview, imageCapture)
    }, ContextCompat.getMainExecutor(this))
    }
    Como utilizar?
    Inicializar a câmera
    CameraActivity.kt

    Executado quando
    a câmera fica
    disponível

    View full-size slide

  76. private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val preview = Preview.Builder().build().also {
    it.setSurfaceProvider(findViewById(R.id.viewFinder).surfaceProvider)
    }
    imageCapture = ImageCapture.Builder().build()
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, DEFAULT_BACK_CAMERA, preview, imageCapture)
    }, ContextCompat.getMainExecutor(this))
    }
    Como utilizar?
    Inicializar a câmera
    CameraActivity.kt

    View full-size slide

  77. private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val preview = Preview.Builder().build().also {
    it.setSurfaceProvider(findViewById(R.id.viewFinder).surfaceProvider)
    }
    imageCapture = ImageCapture.Builder().build()
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, DEFAULT_BACK_CAMERA, preview, imageCapture)
    }, ContextCompat.getMainExecutor(this))
    }
    Como utilizar?
    Inicializar a câmera
    CameraActivity.kt

    View full-size slide

  78. private fun startCamera() {
    val cameraProvider = ProcessCameraProvider.getInstance(this)
    cameraProvider.addListener( {
    val preview = Preview.Builder().build().also {
    it.setSurfaceProvider(findViewById(R.id.viewFinder).surfaceProvider)
    }
    imageCapture = ImageCapture.Builder().build()
    cameraProvider.get().unbindAll()
    cameraProvider.get().bindToLifecycle(this, DEFAULT_BACK_CAMERA, preview, imageCapture)
    }, ContextCompat.getMainExecutor(this))
    }
    Como utilizar?
    Inicializar a câmera
    CameraActivity.kt

    View full-size slide

  79. override fun onCreate(savedInstanceState: Bundle?) {
    ...
    findViewById(R.id.camera_capture_button).setOnClickListener { takePhoto() }
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  80. private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions,
    ContextCompat.getMainExecutor(this),
    object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Toast.makeText(baseContext, "Photo saved", Toast.LENGTH_SHORT).show()
    }
    override fun onError(exception: ImageCaptureException) {}
    })
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  81. private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions,
    ContextCompat.getMainExecutor(this),
    object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Toast.makeText(baseContext, "Photo saved", Toast.LENGTH_SHORT).show()
    }
    override fun onError(exception: ImageCaptureException) {}
    })
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  82. private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions,
    ContextCompat.getMainExecutor(this),
    object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Toast.makeText(baseContext, "Photo saved", Toast.LENGTH_SHORT).show()
    }
    override fun onError(exception: ImageCaptureException) {}
    })
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  83. private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions,
    ContextCompat.getMainExecutor(this),
    object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Toast.makeText(baseContext, "Photo saved", Toast.LENGTH_SHORT).show()
    }
    override fun onError(exception: ImageCaptureException) {}
    })
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  84. private fun takePhoto() {
    val photoFile = File(getOutputDirectory(), "${System.currentTimeMillis()}.jpg")
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(outputOptions,
    ContextCompat.getMainExecutor(this),
    object : ImageCapture.OnImageSavedCallback {
    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Toast.makeText(baseContext, "Photo saved", Toast.LENGTH_SHORT).show()
    }
    override fun onError(exception: ImageCaptureException) {}
    })
    }
    Como utilizar?
    Tirar uma foto
    CameraActivity.kt

    View full-size slide

  85. Compose
    ● Forma inovadora de criar layouts nativos (em Android)
    ● Permite construir componentes gráficos de forma declarativa
    ● Interoperável com os componentes nativos
    ● Reduz a quantidade de código necessário para criar interfaces gráficas
    ● Tira partido das vantagens de Kotlin para facilitar a escrita de código
    α

    View full-size slide


  86. Compose ainda se encontra em alpha

    View full-size slide

  87. Android Studio 4.2 Canary 16
    https://developer.android.com/studio/preview

    View full-size slide

  88. android {
    ...
    buildFeatures { compose true }
    kotlinOptions {
    useIR = true
    }
    }
    dependencies {
    ...
    implementation "androidx.compose.ui:ui:1.0.0-alpha07"
    implementation "androidx.compose.foundation:foundation:1.0.0-alpha07"
    implementation "androidx.ui:ui-tooling:1.0.0-alpha07"
    implementation "androidx.compose.material:material:1.0.0-alpha07"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View full-size slide

  89. @Composable
    fun Example() {
    // O código vem para aqui
    }
    Como utilizar?
    Compose
    Example.kt

    View full-size slide

  90. @Composable
    fun Example() {
    // O código vem para aqui
    }
    @Composable
    @Preview
    fun DefaultExample() {
    Example()
    }
    Como utilizar?
    Compose
    Example.kt

    View full-size slide

  91. @Composable
    fun Example() {
    // O código vem para aqui
    }
    @Composable
    @Preview
    fun DefaultExample() {
    Example()
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  92. @Composable
    fun Example() {
    Text(
    text = "Olá mundo! ",
    style = style = MaterialTheme.typography.h5,
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  93. @Composable
    fun Example() {
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  94. android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding_start="16dp"
    android:text="Olá mundo! "
    android:textColor="@android:color/white"
    android:textSize="19sp"/>
    Como utilizar?
    example.xml
    !Compose

    View full-size slide

  95. android:layout_height="wrap_content"
    android:padding_start="16dp"
    android:text="Olá mundo! "
    android:textColor="@android:color/white"
    android:textSize="19sp"/>
    Como utilizar?
    example.xml
    !Compose
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Example.kt

    View full-size slide

  96. @Composable
    fun Example() {
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Text(
    text = "Bem-vindos ao ATP!",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.grey)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  97. @Composable
    fun Example() {
    Column {
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Text(
    text = "Bem-vindos ao ATP!",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.grey)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  98. @Composable
    fun Example() {
    Column {
    Image(
    asset = vectorResource(id = R.drawable.ic_paw)
    )
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Text(
    text = "Bem-vindos ao ATP!",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.grey)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  99. @Composable
    fun Example() {
    Column {
    Image(
    asset = vectorResource(id = R.drawable.ic_paw),
    modifier = Modifier.width(32.dp).height(32.dp)
    )
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Text(
    text = "Bem-vindos ao ATP!",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.grey)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  100. @Composable
    fun Example() {
    Row {
    Image(
    asset = vectorResource(id = R.drawable.ic_paw),
    modifier = Modifier.width(32.dp).height(32.dp)
    )
    Column {
    Text(
    text = "Olá mundo! ",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorResource(id = R.color.white)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    Text(
    text = "Bem-vindos ao ATP!",
    style = TextStyle(
    fontSize = 19.sp,
    fontWeight = FontWeight.Normal,
    color = colorRsource(id = R.color.grey)
    ),
    modifier = Modifier.padding(start = 16.dp)
    )
    }
    }
    }
    Como utilizar?
    Example.kt
    Compose

    View full-size slide

  101. class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_example)
    }
    }
    Como utilizar?
    ExampleActivity.kt
    Compose

    View full-size slide

  102. class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
    MaterialTheme {
    Column(modifier = Modifier.fillMaxWidth()) {
    TopAppBar(title = { Text(stringResource(id = R.string.example)) })
    Example()
    }
    }
    }
    }
    }
    Como utilizar?
    ExampleActivity.kt
    Compose

    View full-size slide

  103. IADE
    Bruno Nobre
    Universidade Europeia
    Princípios de UX
    para Mobile

    View full-size slide

  104. Abre o Android
    Studio e vamos
    começar a
    programar
    ‍‍

    View full-size slide

  105. interface Printer {
    fun print()
    }
    interface Writer {
    fun write()
    }
    class PrintWriter(
    printer: Printer,
    writer: Writer
    ) : Printer by printer, Writer by writer {}
    Interface delegation

    View full-size slide

  106. class Dog(private val name: String) {
    init {
    println("Dog $name")
    }
    }
    Bloco init
    public class Dog {
    private String name;
    public Dog(String name) {
    this.name = name;
    System.out.println("Dog " + name);
    }
    }

    View full-size slide

  107. class Dog(private val name: String) {
    init {
    println("Dog $name")
    }
    init {
    println("Dog $name")
    }
    }
    Bloco init
    public class Dog {
    private String name;
    public Dog(String name) {
    this.name = name;
    System.out.println("Dog " + name);
    }
    }

    View full-size slide

  108. Sexta-Feira
    negra
    Photo by Xiaolong Wong on Unsplash

    View full-size slide

  109. ctrl shift
    + + v
    Histórico de conteúdo copiado

    View full-size slide

  110. cmd shift
    + + v
    Histórico de conteúdo copiado

    View full-size slide

  111. Histórico de conteúdo copiado

    cmd shift
    + + v

    View full-size slide

  112. Mudar a fonte

    Para mudar a fonte do código:
    1. Carregar em Android Studio na barra de ações
    2. Preferences
    3. Pesquisar por Font
    4. Editor → General → Font
    5. Alterar para a fonte pretendida

    View full-size slide

  113. Mudar a fonte

    Para mudar a fonte do código:
    1. Carregar em Android Studio na barra de ações
    2. Preferences
    3. Pesquisar por Font
    4. Appe... & Behavior → Appearance
    5. Alterar para a fonte pretendida

    View full-size slide

  114. Mudar a fonte

    Para mudar a fonte do código:
    1. Carregar em Android Studio na barra de ações
    2. Preferences
    3. Pesquisar por Font
    4. Appe... & Behavior → Appearance
    5. Alterar para a fonte pretendida

    View full-size slide

  115. Exportar como zip

    1. Carregar em File na barra de ações
    2. Export to Zip File...
    Nenhum dos ficheiros gerados é adicionado ao zip.

    View full-size slide

  116. E se quisermos alterar o comportamento de sempre que
    abrimos um layout o modo design ser a opção
    predefinida?

    View full-size slide

  117. Mudar o modo de visualização

    Para mudar a fonte do código:
    1. Carregar em Android Studio na barra de ações
    2. Preferences
    3. Editor → Layout Editor
    4. Other Resources

    View full-size slide

  118. scrcpy

    O teu telemóvel, no teu computador.
    ● O ecrã do teu telemóvel no teu computador
    ● Permite controlares o teu telemóvel remotamente
    ● Funciona em GNU/Linux, Windows e macOS

    View full-size slide

  119. scrcpy

    O teu telemóvel, no teu computador.
    ● O ecrã do teu telemóvel no teu computador
    ● Permite controlares o teu telemóvel remotamente
    ● Funciona em GNU/Linux, Windows e macOS
    apt install scrcpy //Linux
    choco install scrcpy //Windows
    brew install scrcpy //macOS
    https://github.com/genymobile/scrcpy

    View full-size slide

  120. Trabalho Para Casa
    ‍‍

    View full-size slide

  121. Trabalho para casa
    ● Implementar um modo de navegação diferente
    ○ BottomNavigationView
    ■ Main (MainFragment.kt)
    ■ Lista de raças (ListFragment.kt)
    ■ Raças favoritas (FavoritesFragment.kt)
    ■ About (AboutFragment.kt)

    View full-size slide

  122. ● Implementar um novo ecrã - FavoritesFragment.kt
    ● Contém todos as raças favoritas do utilizador
    ● Permite adicionar/remover uma raça aos/dos favoritos
    Trabalho para casa

    View full-size slide

  123. Continuamos a
    responder no
    discord

    View full-size slide

  124. Obrigado
    ‍♀

    View full-size slide

  125. Android
    Training
    Program
    PORTUGAL
    Aula #7
    Firebase
    Próxima aula: 02 de Dezembro

    View full-size slide