diff --git a/solutions/devsprint-bruno-almeida-2/app/build.gradle b/solutions/devsprint-bruno-almeida-2/app/build.gradle index f80f5fa..ad2f48a 100644 --- a/solutions/devsprint-bruno-almeida-2/app/build.gradle +++ b/solutions/devsprint-bruno-almeida-2/app/build.gradle @@ -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' diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/AndroidManifest.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/AndroidManifest.xml index a7e0586..25bce48 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/AndroidManifest.xml +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ - + - + + + + + + @@ -21,10 +34,9 @@ - + android:exported="true" /> +} \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/datasource/RocketDetailsDataSource.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/datasource/RocketDetailsDataSource.kt new file mode 100644 index 0000000..63ea704 --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/datasource/RocketDetailsDataSource.kt @@ -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] + } +} \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/model/RocketDetailResponse.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/model/RocketDetailResponse.kt new file mode 100644 index 0000000..0bb8782 --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/model/RocketDetailResponse.kt @@ -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, + val description: String +) : Serializable \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/repository/RocketDetailsRepository.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/repository/RocketDetailsRepository.kt new file mode 100644 index 0000000..fe756df --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/data/repository/RocketDetailsRepository.kt @@ -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 { + return handleResult(rocketDetailsDataSource.getRocketDetails(id)) + } + + private fun handleResult(rocketDetails: RocketDetailResponse): Result { + return try { + Result.success(rocketDetails) + } catch (e: Exception) { + Result.failure(e) + } + } +} + + + + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchActivity.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchActivity.kt index 5fb552c..8da119e 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchActivity.kt +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchActivity.kt @@ -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()) { + // 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() + } } \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsActivity.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsActivity.kt index 9734b50..d1f9c3b 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsActivity.kt +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsActivity.kt @@ -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() { @@ -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) } } \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsFragment.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsFragment.kt index f1e21b4..dded108 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsFragment.kt +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/LaunchDetailsFragment.kt @@ -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 @@ -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? @@ -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 { diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsActivity.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsActivity.kt index a3781e9..e82de84 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsActivity.kt +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsActivity.kt @@ -1,4 +1,19 @@ package com.devpass.spaceapp.presentation -class RocketDetailsActivity { -} \ No newline at end of file +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) + + } +} + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsViewModel.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsViewModel.kt new file mode 100644 index 0000000..494ce6d --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/RocketDetailsViewModel.kt @@ -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>() + val rocketDetail: LiveData> + get() = _rocketDetail + + fun getRocketDetail(id: String){ + viewModelScope.launch { + _rocketDetail.value = repository.getRocketDetails(id) + } + } +} + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/nextlaunches/NextLaunchesActivity.kt b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/nextlaunches/NextLaunchesActivity.kt index 9aeaea5..1129249 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/nextlaunches/NextLaunchesActivity.kt +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/java/com/devpass/spaceapp/presentation/nextlaunches/NextLaunchesActivity.kt @@ -6,15 +6,13 @@ import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.devpass.spaceapp.R import com.devpass.spaceapp.RetrofitService import com.devpass.spaceapp.data.api.SpaceXAPIClient import com.devpass.spaceapp.data.datasource.LaunchesListDataSourceImpl import com.devpass.spaceapp.data.model.NextLaunchesModel import com.devpass.spaceapp.data.repository.LaunchesListRepositoryImpl import com.devpass.spaceapp.databinding.ActivityMainBinding -import com.devpass.spaceapp.presentation.LaunchDetailsActivity +import com.devpass.spaceapp.presentation.LaunchActivity import com.devpass.spaceapp.presentation.NextLaunchesAdapter class NextLaunchesActivity : AppCompatActivity() { @@ -53,7 +51,7 @@ class NextLaunchesActivity : AppCompatActivity() { } private fun onLaunchClick(launch: NextLaunchesModel) { - val intent = Intent(this, LaunchDetailsActivity::class.java) + val intent = Intent(this, LaunchActivity::class.java) intent.putExtra("nextLaunch", launch) // <- corrigido para "nextLaunch" startActivity(intent) } diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/drawable/ic_back_arrow.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/drawable/ic_back_arrow.xml new file mode 100644 index 0000000..31e7df2 --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/drawable/ic_back_arrow.xml @@ -0,0 +1,5 @@ + + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch.xml new file mode 100644 index 0000000..53d42fb --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch_details.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch_details.xml index 50951f3..4c1e6dc 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch_details.xml +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_launch_details.xml @@ -1,106 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - + tools:context=".presentation.LaunchDetailsActivity"> + + + + + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_rocket_details.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_rocket_details.xml new file mode 100644 index 0000000..2b6d12c --- /dev/null +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/activity_rocket_details.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/fragment_launch_details.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/fragment_launch_details.xml index c69cf5f..e4b06d7 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/fragment_launch_details.xml +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/layout/fragment_launch_details.xml @@ -1,13 +1,35 @@ - + android:layout_height="wrap_content" + app:cardCornerRadius="4dp" + app:cardElevation="4dp" + app:cardUseCompatPadding="true"> - + android:layout_height="match_parent" + android:padding="16dp" + android:orientation="vertical"> - + + + + + + diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/strings.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/strings.xml index a982fb7..bb9b539 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/strings.xml +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/strings.xml @@ -1,10 +1,15 @@ SpaceApp Space App - 0 Descricao da imagem imagem da api titulo data status + Nome da missão desconhecido + Erro ao carregar detalhes + View More + Details + Launchpad + Rocket \ No newline at end of file diff --git a/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/themes.xml b/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/themes.xml index 9f202d4..53fa429 100644 --- a/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/themes.xml +++ b/solutions/devsprint-bruno-almeida-2/app/src/main/res/values/themes.xml @@ -12,6 +12,5 @@ ?attr/colorPrimaryVariant - @style/ToolbarStyle \ No newline at end of file