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. • 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
  2. Andres-Leonardo Martinez-Ortiz Google Carlos Mota Formador Renato Almeida Formador @davilagrau

    @cafonsomota @tallnato Equipa Daniela Ferreira Gestora de comunidades
  3. • 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
  4. #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 ✅ ✅
  5. Sumário Photo by Mika Baumeister on Unsplash • Resumo da

    aula anterior • Jetpack • Room • CameraX • Compose • Kotlin para principiantes • Sexta-Feira negra
  6. • É 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
  7. • 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
  8. 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 )
  9. • Biblioteca de conversão de JSON para Kotlin e Java

    • Optimizada para Android • Compatível Retrofit Moshi
  10. • 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
  11. 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?
  12. 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.
  13. 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.
  14. “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
  15. “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
  16. • 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
  17. 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
  18. 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
  19. @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
  20. @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
  21. @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
  22. @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
  23. @Dao interface DogDAO { @Query("SELECT * FROM dog ORDER BY

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

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

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

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

    name ASC") fun getAlphabetizedDogs(): LiveData<List<DogModel>> @Insert(onConflict = OnConflictStrategy.IGNORE) fun insert(dog: Dog) @Query("DELETE FROM dog") fun deleteAll() } Como utilizar? Definir o DAO (Data access object) DogDAO.kt
  28. @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
  29. @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
  30. @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
  31. @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
  32. @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
  33. class DogRepository(private val dogDao: DogDAO) { val allDogs: LiveData<List<DogModel>> =

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

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

    dogDao.getAlphabetizedDogs() fun insert(dog: DogModel) { KennelDatabase.databaseWriteExecutor .execute { dogDao.insert(dog) } } } Como utilizar? Repositório DogRepository.kt
  36. class MainViewModel( private val repository: DogRepository ) : ViewModel() {

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

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

    ... fun getDogsFromDatabase(): LiveData<List<DogModel>> { return repository.allDogs } fun addDog(dog : Dog) { repository.insert(toDogModel(dog)) } } Como utilizar? No ViewModel MainViewModel.kt
  39. class MainViewModelFactory( private val repository: DogRepository ) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): 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
  40. class BobiApplication: Application() { val database by lazy { KennelDatabase.getDatabase(this)

    } val repository by lazy { DogRepository(database.dogDao()) } } Como utilizar? O Application BobiApplication.kt
  41. class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by

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

    viewModels { MainViewModelFactory((application as BobiApplication).repository) } ... } Como utilizar? Na Activity MainActivity.kt
  43. 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
  44. 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) }
  45. 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...
  46. 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<Button>(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<PreviewView>(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) } }) } }
  47. 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<Button>(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<PreviewView>(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) } }) } } ‍♀
  48. 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<Button>(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<PreviewView>(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
  49. 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
  50. class CameraActivity : AppCompatActivity() { ... } <activity android:name=".presentation.ui.CameraActivity" />

    Como utilizar? Criar uma nova Activity AndroidManifest.xml CameraActivity.kt
  51. 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
  52. 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
  53. override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, 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
  54. override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, 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
  55. <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout ... <ImageButton android:id="@+id/camera_capture_button" ... <androidx.camera.view.PreviewView android:id="@+id/viewFinder"

    android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout> Como utilizar? Definir o layout activity_camera.kt
  56. <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout ... <ImageButton android:id="@+id/camera_capture_button" ... <androidx.camera.view.PreviewView android:id="@+id/viewFinder"

    android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout> Como utilizar? Definir o layout activity_camera.kt
  57. private fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this) cameraProvider.addListener( {

    val preview = Preview.Builder().build().also { it.setSurfaceProvider(findViewById<PreviewView>(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
  58. private fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this) cameraProvider.addListener( {

    val preview = Preview.Builder().build().also { it.setSurfaceProvider(findViewById<PreviewView>(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
  59. private fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this) cameraProvider.addListener( {

    val preview = Preview.Builder().build().also { it.setSurfaceProvider(findViewById<PreviewView>(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
  60. private fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this) cameraProvider.addListener( {

    val preview = Preview.Builder().build().also { it.setSurfaceProvider(findViewById<PreviewView>(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
  61. private fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this) cameraProvider.addListener( {

    val preview = Preview.Builder().build().also { it.setSurfaceProvider(findViewById<PreviewView>(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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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 α
  68. 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
  69. @Composable fun Example() { // O código vem para aqui

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

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

    } @Composable @Preview fun DefaultExample() { Example() } Como utilizar? Example.kt Compose
  72. @Composable fun Example() { Text( text = "Olá mundo! ",

    style = style = MaterialTheme.typography.h5, modifier = Modifier.padding(start = 16.dp) ) } Como utilizar? Example.kt Compose
  73. @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
  74. <TextView 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 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
  75. @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
  76. @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
  77. @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
  78. @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
  79. @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
  80. class ExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_example) } } Como utilizar? ExampleActivity.kt Compose
  81. 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
  82. interface Printer { fun print() } interface Writer { fun

    write() } class PrintWriter( printer: Printer, writer: Writer ) : Printer by printer, Writer by writer {} Interface delegation
  83. 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); } }
  84. 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); } }
  85. 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
  86. 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
  87. 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
  88. Exportar como zip 1. Carregar em File na barra de

    ações 2. Export to Zip File... Nenhum dos ficheiros gerados é adicionado ao zip.
  89. E se quisermos alterar o comportamento de sempre que abrimos

    um layout o modo design ser a opção predefinida? ‍
  90. 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
  91. 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
  92. 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
  93. 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)
  94. • 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