Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### 環境

- IDE:Android Studio Ladybug | 2024.2.1 Patch 3
- Kotlin: 2.0.0
- Kotlin: 2.0.21
- Java:17
- Gradle:8.9
- minSdk:23
Expand Down
13 changes: 10 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
id 'kotlin-parcelize'
id 'androidx.navigation.safeargs.kotlin'
id("org.jlleitschuh.gradle.ktlint") version "12.1.2"
id 'com.google.dagger.hilt.android'
}

android {
Expand All @@ -28,11 +29,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '17'
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
Expand Down Expand Up @@ -63,10 +64,16 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation "com.google.dagger:hilt-android:2.55"
kapt "com.google.dagger:hilt-compiler:2.55"

// Retrofit
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
}

kapt {
correctErrorTypes true
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AndroidEngineerCodeCheck"
android:fullBackupContent="@xml/backup_descriptor">
android:fullBackupContent="@xml/backup_descriptor"
android:name=".CodeCheckApplication">
<activity
android:name="jp.co.yumemi.android.code_check.TopActivity"
android:exported="true">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package jp.co.yumemi.android.code_check

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class CodeCheckApplication : Application()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package jp.co.yumemi.android.code_check

import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import jp.co.yumemi.android.code_check.features.github.api.GitHubServiceApi
import jp.co.yumemi.android.code_check.features.github.api.GitHubServiceApiImpl
import jp.co.yumemi.android.code_check.features.github.reposiotory.GitHubServiceRepository
import jp.co.yumemi.android.code_check.features.github.reposiotory.GitHubServiceRepositoryImpl
import jp.co.yumemi.android.code_check.features.github.usecase.GitHubServiceUsecase
import jp.co.yumemi.android.code_check.features.github.usecase.GitHubServiceUsecaseImpl
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class GitHubUsecaseModule {
@Singleton
@Binds
abstract fun provideGitHubServiceUsecase(impl: GitHubServiceUsecaseImpl): GitHubServiceUsecase
}

@Module
@InstallIn(SingletonComponent::class)
abstract class GitHubRepositoryModule {
@Singleton
@Binds
abstract fun provideGitHubServiceRepository(impl: GitHubServiceRepositoryImpl): GitHubServiceRepository

companion object {
@Provides
@Singleton
fun provideGitHubServiceApi(): GitHubServiceApi {
return GitHubServiceApiImpl()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
package jp.co.yumemi.android.code_check

import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class TopActivity : AppCompatActivity(R.layout.activity_top)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package jp.co.yumemi.android.code_check.core.api

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class NetworkConnectivityService
@Inject
constructor(
@ApplicationContext private val context: Context,
) {
fun isNetworkAvailable(): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package jp.co.yumemi.android.code_check
package jp.co.yumemi.android.code_check.core.entity

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/*
* Copyright © 2021 YUMEMI Inc. All rights reserved.
*/
package jp.co.yumemi.android.code_check
package jp.co.yumemi.android.code_check.core.presenter.detail

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import coil.load
import dagger.hilt.android.AndroidEntryPoint
import jp.co.yumemi.android.code_check.R
import jp.co.yumemi.android.code_check.core.entity.RepositoryItem
import jp.co.yumemi.android.code_check.databinding.FragmentRepositoryDetailBinding

@AndroidEntryPoint
class RepositoryDetailFragment : Fragment(R.layout.fragment_repository_detail) {
private val args: RepositoryDetailFragmentArgs by navArgs()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package jp.co.yumemi.android.code_check
package jp.co.yumemi.android.code_check.core.presenter.search

import android.view.LayoutInflater
import android.view.View
Expand All @@ -7,6 +7,8 @@ import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import jp.co.yumemi.android.code_check.R
import jp.co.yumemi.android.code_check.core.entity.RepositoryItem

/**
* DiffUtilの実装
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
/*
* Copyright © 2021 YUMEMI Inc. All rights reserved.
*/
package jp.co.yumemi.android.code_check
package jp.co.yumemi.android.code_check.core.presenter.search

import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import jp.co.yumemi.android.code_check.R
import jp.co.yumemi.android.code_check.core.entity.RepositoryItem
import jp.co.yumemi.android.code_check.core.utils.DialogHelper
import jp.co.yumemi.android.code_check.databinding.FragmentRepositorySearchBinding

@AndroidEntryPoint
class RepositorySearchFragment : Fragment(R.layout.fragment_repository_search) {
private var _binding: FragmentRepositorySearchBinding? = null
private val binding get() = _binding ?: throw IllegalStateException("Binding is null")
private lateinit var viewModel: RepositorySearchViewModel
private val viewModel: RepositorySearchViewModel by viewModels()

private val adapter by lazy {
RepositoryListRecyclerViewAdapter(
Expand All @@ -34,7 +36,6 @@ class RepositorySearchFragment : Fragment(R.layout.fragment_repository_search) {
) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentRepositorySearchBinding.bind(view)
viewModel = ViewModelProvider(this)[RepositorySearchViewModel::class.java]

observeViewModel()
setupRecyclerView()
Expand All @@ -46,13 +47,19 @@ class RepositorySearchFragment : Fragment(R.layout.fragment_repository_search) {
adapter.submitList(it)
}
viewModel.errorMessage.observe(viewLifecycleOwner) {
it?.let { DialogHelper.showErrorDialog(requireContext(), it) }
it?.let {
DialogHelper.showErrorDialog(
requireContext(),
requireContext().getString(it)
)
}
}
}

private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(requireContext())
val dividerItemDecoration = DividerItemDecoration(requireContext(), layoutManager.orientation)
val dividerItemDecoration =
DividerItemDecoration(requireContext(), layoutManager.orientation)

binding.recyclerView.apply {
this.layoutManager = layoutManager
Expand All @@ -64,7 +71,7 @@ class RepositorySearchFragment : Fragment(R.layout.fragment_repository_search) {
private fun setupSearchInput() {
binding.searchInputText.setOnEditorActionListener { editText, action, _ ->
if (action == EditorInfo.IME_ACTION_SEARCH) {
viewModel.searchRepositories(editText.text.toString().trim())
viewModel.searchRepositories(editText.text.toString().trim(), requireContext())
true
} else {
false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package jp.co.yumemi.android.code_check.core.presenter.search

import android.content.Context
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import jp.co.yumemi.android.code_check.R
import jp.co.yumemi.android.code_check.core.entity.RepositoryItem
import jp.co.yumemi.android.code_check.features.github.reposiotory.NetworkException
import jp.co.yumemi.android.code_check.features.github.usecase.GitHubServiceUsecase
import jp.co.yumemi.android.code_check.features.github.utils.GitHubError
import jp.co.yumemi.android.code_check.features.github.utils.NetworkResult
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
* RepositorySearchFragmentで利用するリポジトリ検索用のViewModel
*/
@HiltViewModel
class RepositorySearchViewModel
@Inject
constructor(
private val networkRepository: GitHubServiceUsecase,
) : ViewModel() {
private val _errorMessage = MutableLiveData<Int?>()
val errorMessage: LiveData<Int?> get() = _errorMessage

private val _searchResults = MutableLiveData<List<RepositoryItem>>()
val searchResults: LiveData<List<RepositoryItem>> get() = _searchResults

/**
* GitHubのレポジトリ検索を行う
* @param query 検索キーワード
* @param context コンテキスト
*/
fun searchRepositories(
query: String,
context: Context,
) {
if (query.isBlank()) {
_errorMessage.postValue(R.string.form_is_empty)
return
}
viewModelScope.launch {
try {
val results = networkRepository.fetchSearchResults(query)
if (results is NetworkResult.Error) {
handleError(results.exception)
return@launch
}
if (results is NetworkResult.Success) {
_searchResults.postValue(results.data)
}
} catch (e: NetworkException) {
Log.e("NetworkException", e.message, e)
handleError(GitHubError.NetworkError(e))
}
}
}

/**
* エラーが発生した時に、Viewに問題を表示するためのもの
* @param GitHubError エラー情報
* @param context コンテキスト
*/
private fun handleError(error: GitHubError) {
_errorMessage.value =
when (error) {
is GitHubError.NetworkError -> R.string.network_error
is GitHubError.ApiError -> R.string.api_error
is GitHubError.ParseError -> R.string.parse_error
is GitHubError.RateLimitError -> R.string.rate_limit_error
is GitHubError.AuthenticationError -> R.string.auth_error
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package jp.co.yumemi.android.code_check
package jp.co.yumemi.android.code_check.core.utils

import android.app.AlertDialog
import android.content.Context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package jp.co.yumemi.android.code_check.features.github.api

import jp.co.yumemi.android.code_check.features.github.entity.RepositoryList

interface GitHubRepositoryApi {
interface GitHubServiceApi {
suspend fun getRepository(searchWord: String): RepositoryList
}
Loading
Loading