Skip to content
4 changes: 4 additions & 0 deletions solutions/devsprint-bruno-almeida-2/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ dependencies {
//Material Design para tabs
implementation 'com.google.android.material:material:1.4.0'

//lifecycle viewmodel
implementation "androidx.activity:activity-ktx:1.3.1"
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'

//Viewpager
implementation 'androidx.viewpager2:viewpager2:1.0.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.devpass.spaceapp">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
Expand All @@ -11,7 +11,20 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SpaceApp">

<activity
android:name=".presentation.RocketDetailsActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".presentation.LaunchActivity"
android:exported="false">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".presentation.nextlaunches.NextLaunchesActivity"
android:exported="true">
Expand All @@ -21,10 +34,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".presentation.LaunchDetailsActivity"
android:exported="true"/>
android:exported="true" />

<meta-data
android:name="preloaded_fonts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.devpass.spaceapp

import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

const val BASE_URL = "https://api.spacexdata.com/v5/"
const val BASE_URL = "https://api.spacexdata.com/v4/"

object RetrofitService {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.devpass.spaceapp.data.api

import com.devpass.spaceapp.data.model.RocketDetailResponse
import retrofit2.http.GET
import retrofit2.http.Query

interface RocketdetailAPIClient {

@GET("rockets")
suspend fun getRocketDetails(@Query("id") id: String): List<RocketDetailResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.devpass.spaceapp.data.datasource

import com.devpass.spaceapp.data.api.RocketdetailAPIClient
import com.devpass.spaceapp.data.model.RocketDetailResponse

class RocketDetailsDataSource(private val rocketdetailAPIClient: RocketdetailAPIClient) {
suspend fun getRocketDetails(id: String) : RocketDetailResponse {
return rocketdetailAPIClient.getRocketDetails(id)[0]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.devpass.spaceapp.data.model

import java.io.Serializable

data class RocketDetailResponse(
val id: String,
val name: String,
val flickr_images: List<String>,
val description: String
) : Serializable
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.devpass.spaceapp.data.repository

import com.devpass.spaceapp.data.datasource.RocketDetailsDataSource
import com.devpass.spaceapp.data.model.RocketDetailResponse

class RocketDetailsRepository(private val rocketDetailsDataSource: RocketDetailsDataSource) {

suspend fun getRocketDetails(id: String): Result<RocketDetailResponse> {
return handleResult(rocketDetailsDataSource.getRocketDetails(id))
}

private fun handleResult(rocketDetails: RocketDetailResponse): Result<RocketDetailResponse> {
return try {
Result.success(rocketDetails)
} catch (e: Exception) {
Result.failure(e)
}
}
}





Original file line number Diff line number Diff line change
@@ -1,4 +1,83 @@
package com.devpass.spaceapp.presentation

class LaunchActivity {
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

import com.bumptech.glide.Glide
import com.devpass.spaceapp.R
import com.devpass.spaceapp.RetrofitService
import com.devpass.spaceapp.data.api.RocketdetailAPIClient
import com.devpass.spaceapp.data.datasource.RocketDetailsDataSource
import com.devpass.spaceapp.data.model.NextLaunchesModel
import com.devpass.spaceapp.data.repository.RocketDetailsRepository

import com.devpass.spaceapp.databinding.ActivityLaunchBinding
import com.google.android.material.tabs.TabLayoutMediator

class LaunchActivity : AppCompatActivity() {

private val service = RetrofitService.retrofit.create(RocketdetailAPIClient::class.java)
private val rocketDetailsDataSource = RocketDetailsDataSource(service)
private val repository:RocketDetailsRepository = RocketDetailsRepository(rocketDetailsDataSource)
private val rocketDetailsViewModel = RocketDetailsViewModel(repository)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityLaunchBinding.inflate(layoutInflater)
setContentView(binding.root)

// Recuperar o objeto launch da intent
val launch = intent.getSerializableExtra("nextLaunch") as NextLaunchesModel

// Preencher os campos com as informações do objeto launch
binding.apply {
launchTitleTextView.text = launch.name
launchDateTextView.text = launch.date_utc
launchStatusTextView.text = launch.status.toString()
}

if(launch.links.image.small.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Não está errado, mas nesse caso a gente pode colocar a função no Glide .error("drawable desejado") assim a gente evita de fazer um if sem "tanta necessidade"

// caso a URL da imagem seja nula ou vazia, carrega uma imagem padrão
Glide.with(this)
.load(R.drawable.space_logo)
.circleCrop()
.into(binding.launchImage)
} else {
// carrega a imagem usando Glide
Glide.with(this)
.load(launch.links.image.small)
.circleCrop()
.into(binding.launchImage)
}

rocketDetailsViewModel.rocketDetail.observe(this) { result ->
result.onSuccess { rocketDetail ->
Log.d("LaunchActivity", "Rocket Detail: $rocketDetail")
// faça algo com os dados de RocketDetail
}.onFailure { exception ->
Log.e("LaunchActivity", "Failed to fetch rocket detail", exception)
// trate o erro
}
}

rocketDetailsViewModel.getRocketDetail(launch.id)

// Cria uma instância do LaunchDetailsPagerAdapter com as informações do lançamento
val launchDetailsPagerAdapter = LaunchDetailsPagerAdapter(this, launch)

// Configura o ViewPager para usar o adapter
binding.viewPager.adapter = launchDetailsPagerAdapter

// Adiciona as abas ao TabLayout
val titles = arrayOf(getString(R.string.tab_title_details), getString(R.string.tab_title_launchpad), getString(R.string.tab_title_rocket))
for (title in titles) {
binding.tabLayout.addTab(binding.tabLayout.newTab().setText(title))
}

// Conecta o TabLayout e ViewPager
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
tab.text = titles[position]
}.attach()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.devpass.spaceapp.presentation

import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.devpass.spaceapp.R
import com.devpass.spaceapp.data.model.NextLaunchesModel
import com.devpass.spaceapp.databinding.ActivityLaunchDetailsBinding
import com.google.android.material.tabs.TabLayoutMediator

private const val ARG_LAUNCH = "fullLaunchDescription"

class LaunchDetailsActivity : AppCompatActivity() {

Expand All @@ -15,42 +15,28 @@ class LaunchDetailsActivity : AppCompatActivity() {
val binding = ActivityLaunchDetailsBinding.inflate(layoutInflater)
setContentView(binding.root)

// Recuperar o objeto launch da intent
val launch = intent.getSerializableExtra("nextLaunch") as NextLaunchesModel
// Configurar a Toolbar como a ActionBar da Activity
setSupportActionBar(binding.toolbar2)

// Preencher os campos com as informações do objeto launch
binding.launchTitleTextView.text = launch.name
binding.launchDateTextView.text = launch.date_utc.toString()
binding.launchStatusTextView.text = launch.status.toString()
if(launch.links.image.small.isEmpty()) {
// caso a URL da imagem seja nula ou vazia, carrega uma imagem padrão
Glide.with(this)
.load(R.drawable.space_logo)
.circleCrop()
.into(binding.launchImage)
} else {
// carrega a imagem usando Glide
Glide.with(this)
.load(launch.links.image.small)
.circleCrop()
.into(binding.launchImage)
}
// Habilitar o botão de voltar na Toolbar
supportActionBar?.setDisplayHomeAsUpEnabled(true)

// Cria uma instância do LaunchDetailsPagerAdapter com as informações do lançamento
val launchDetailsPagerAdapter = LaunchDetailsPagerAdapter(this, launch)
// Definir o ícone personalizado de retorno na Toolbar
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_back_arrow)

// Configura o ViewPager para usar o adapter
binding.viewPager.adapter = launchDetailsPagerAdapter
// Recuperar o objeto launch da intent
val details = intent.getCharSequenceExtra(ARG_LAUNCH)

// Adiciona as abas ao TabLayout
val titles = arrayOf("Overview", "Mission", "Rocket")
for (title in titles) {
binding.tabLayout.addTab(binding.tabLayout.newTab().setText(title))
}
// Preencher os campos com as informações do objeto launch
binding.launchDescriptionTextView.text = details
}

// Conecta o TabLayout e ViewPager
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
tab.text = titles[position]
}.attach()
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
// Quando o botão de voltar na Toolbar é pressionado, finaliza a activity atual
finish()
return true
}
return super.onOptionsItemSelected(item)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.devpass.spaceapp.presentation

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.devpass.spaceapp.R
import com.devpass.spaceapp.data.model.NextLaunchesModel
import com.devpass.spaceapp.databinding.FragmentLaunchDetailsBinding

Expand All @@ -16,7 +18,6 @@ class LaunchDetailsFragment : Fragment() {
private var _binding: FragmentLaunchDetailsBinding? = null
private val binding get() = _binding!!


override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
Expand All @@ -31,8 +32,21 @@ class LaunchDetailsFragment : Fragment() {
// Recupera o objeto NextLaunchesModel dos argumentos
val launch = arguments?.getSerializable(ARG_LAUNCH) as NextLaunchesModel?

// Exibe o nome da missão em um TextView
binding.launchDescription.text = launch?.name ?: "Nome da missão desconhecido"
val fullDescription = launch?.details ?: getString(R.string.details_load_error)

binding.launchDescriptionFragment.text = fullDescription

val viewMore = getString(R.string.view_more)

val viewMoreLink = binding.launchViewMore

viewMoreLink.text = viewMore

viewMoreLink.setOnClickListener {
val intent = Intent(context, LaunchDetailsActivity::class.java)
intent.putExtra("fullLaunchDescription", fullDescription)
startActivity(intent)
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
package com.devpass.spaceapp.presentation

class RocketDetailsActivity {
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

import com.devpass.spaceapp.databinding.ActivityRocketDetailsBinding

class RocketDetailsActivity : AppCompatActivity() {
private lateinit var binding: ActivityRocketDetailsBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRocketDetailsBinding.inflate(layoutInflater)
setContentView(binding.root)

}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.devpass.spaceapp.presentation

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.devpass.spaceapp.data.model.RocketDetailResponse
import com.devpass.spaceapp.data.repository.RocketDetailsRepository

import androidx.lifecycle.LiveData

import androidx.lifecycle.viewModelScope

import kotlinx.coroutines.launch

class RocketDetailsViewModel(private val repository: RocketDetailsRepository) : ViewModel() {

private val _rocketDetail = MutableLiveData<Result<RocketDetailResponse>>()
val rocketDetail: LiveData<Result<RocketDetailResponse>>
get() = _rocketDetail

fun getRocketDetail(id: String){
viewModelScope.launch {
_rocketDetail.value = repository.getRocketDetails(id)
}
}
}


Loading