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

Android Training Program - Portugal, Aula 6

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for ATP Portugal 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

Avatar for ATP Portugal

ATP Portugal

November 18, 2020

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