diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1bec35e..fc3105b 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,8 @@
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index af0bbdd..703e5d4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/app/build.gradle b/app/build.gradle
index aaaa8ef..aa01acd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,13 +5,19 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
- compileSdkVersion 28
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
defaultConfig {
- applicationId "com.decathlon.android.apptest"
- minSdkVersion 21
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
+ applicationId rootProject.ext.applicationId
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion rootProject.ext.targetSdkVersion
+ versionCode rootProject.ext.appVersionCode
+ versionName rootProject.ext.appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -23,12 +29,36 @@ android {
}
dependencies {
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.0.2'
- implementation 'androidx.core:core-ktx:1.0.2'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+
+ //Androidx libraries
+ implementation "androidx.appcompat:appcompat:$appCompatVersion"
+ implementation "androidx.core:core-ktx:$coreKtxVersion"
+ implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
+ implementation "androidx.legacy:legacy-support-v4:$androidxLegacySupportV4Version"
+ implementation "androidx.recyclerview:recyclerview:$recyclerViewVersion"
+
+ //Design Libraries
+ implementation "com.google.android.material:material:$materialVersion"
+
+ //Network libraries
+ implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
+ implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
+ implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion"
+ implementation "com.squareup.moshi:moshi:$moshiVersion"
+ implementation "com.squareup.okhttp3:logging-interceptor:$okHttpVersion"
+ implementation "com.github.bumptech.glide:glide:$glideVersion"
+ annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
+
+ //Rx Libraries
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
+ implementation "com.jakewharton.rxbinding3:rxbinding:$rxBindingsVersion"
+
+ //Test libraries
+ testImplementation "junit:junit:$junitVersion"
+ androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion"
+ androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
}
diff --git a/app/src/androidTest/java/com/decathlon/android/apptest/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/decathlon/android/apptest/ExampleInstrumentedTest.kt
index 2ef840e..c32c16d 100644
--- a/app/src/androidTest/java/com/decathlon/android/apptest/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/decathlon/android/apptest/ExampleInstrumentedTest.kt
@@ -2,12 +2,10 @@ package com.decathlon.android.apptest
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
-
+import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 24268c3..516f732 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,9 @@
+
+
+
-
+
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/base/BasePresenter.kt b/app/src/main/java/com/decathlon/android/apptest/common/base/BasePresenter.kt
new file mode 100644
index 0000000..8f02e95
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/base/BasePresenter.kt
@@ -0,0 +1,5 @@
+package com.decathlon.android.apptest.common.base
+
+interface BasePresenter {
+ fun onDestroy()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/base/BaseView.kt b/app/src/main/java/com/decathlon/android/apptest/common/base/BaseView.kt
new file mode 100644
index 0000000..a1c39da
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/base/BaseView.kt
@@ -0,0 +1,5 @@
+package com.decathlon.android.apptest.common.base
+
+interface BaseView {
+ var presenter: T
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/base/SingleUseCase.kt b/app/src/main/java/com/decathlon/android/apptest/common/base/SingleUseCase.kt
new file mode 100644
index 0000000..48f801c
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/base/SingleUseCase.kt
@@ -0,0 +1,51 @@
+package com.decathlon.android.apptest.common.base
+
+import io.reactivex.Single
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import io.reactivex.observers.DisposableSingleObserver
+import io.reactivex.schedulers.Schedulers
+
+/**
+ * Abstract class for a UseCase that returns an instance of a [Single].
+ */
+abstract class SingleUseCase {
+
+ private val disposables = CompositeDisposable()
+
+ /**
+ * Builds a [Single] which will be used when the current [SingleUseCase] is executed.
+ */
+ protected abstract fun buildUseCaseObservable(params: Params? = null): Single
+
+ /**
+ * Executes the current use case.
+ *
+ * @param observer {@link DisposableSingleObserver} which will be listening to the single build
+ * by {@link #buildUseCaseObservable(Params)} ()} method.
+ * @param params Parameters (Optional) used to build/execute this use case.
+ */
+ open fun execute(singleObserver: DisposableSingleObserver, params: Params? = null) {
+ val single = this.buildUseCaseObservable(params)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ addDisposable(single.subscribeWith(singleObserver))
+ }
+
+ /**
+ * Dispose from current [CompositeDisposable].
+ */
+ fun dispose() {
+ if (!disposables.isDisposed) {
+ disposables.dispose()
+ }
+ }
+
+ /**
+ * Dispose from current [CompositeDisposable].
+ */
+ private fun addDisposable(disposable: Disposable) {
+ disposables.add(disposable)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/base/UseCase.kt b/app/src/main/java/com/decathlon/android/apptest/common/base/UseCase.kt
new file mode 100644
index 0000000..6967328
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/base/UseCase.kt
@@ -0,0 +1,51 @@
+package com.decathlon.android.apptest.common.base
+
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import io.reactivex.observers.DisposableObserver
+import io.reactivex.schedulers.Schedulers
+
+/**
+ * Abstract class for a UseCase that returns an instance of a [Observable].
+ */
+abstract class UseCase {
+
+ private val disposables = CompositeDisposable()
+
+ /**
+ * Builds an {@link Observable} which will be used when executing the current {@link UseCase}.
+ */
+ abstract fun buildUseCaseObservable(params: Params?): Observable
+
+ /**
+ * Executes the current use case.
+ *
+ * @param observer {@link DisposableObserver} which will be listening to the observable build
+ * by {@link #buildUseCaseObservable(Params)} ()} method.
+ * @param params Parameters (Optional) used to build/execute this use case.
+ */
+ fun execute(observer: DisposableObserver, params: Params?) {
+ val observable = this.buildUseCaseObservable(params)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ addDisposable(observable.subscribeWith(observer))
+ }
+
+ /**
+ * Dispose from current [CompositeDisposable].
+ */
+ fun dispose() {
+ if (!disposables.isDisposed) {
+ disposables.dispose()
+ }
+ }
+
+ /**
+ * Dispose from current [CompositeDisposable].
+ */
+ private fun addDisposable(disposable: Disposable) {
+ disposables.add(disposable)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/exception/EmptyBodyException.kt b/app/src/main/java/com/decathlon/android/apptest/common/exception/EmptyBodyException.kt
new file mode 100644
index 0000000..3114b0e
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/exception/EmptyBodyException.kt
@@ -0,0 +1,3 @@
+package com.decathlon.android.apptest.common.exception
+
+class EmptyBodyException : Exception()
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/exception/ForbiddenException.kt b/app/src/main/java/com/decathlon/android/apptest/common/exception/ForbiddenException.kt
new file mode 100644
index 0000000..1b92005
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/exception/ForbiddenException.kt
@@ -0,0 +1,3 @@
+package com.decathlon.android.apptest.common.exception
+
+class ForbiddenException : Exception()
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/exception/NoConnectivityException.kt b/app/src/main/java/com/decathlon/android/apptest/common/exception/NoConnectivityException.kt
new file mode 100644
index 0000000..78c4a67
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/exception/NoConnectivityException.kt
@@ -0,0 +1,5 @@
+package com.decathlon.android.apptest.common.exception
+
+import java.io.IOException
+
+class NoConnectivityException : IOException()
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/exception/UnknownErrorException.kt b/app/src/main/java/com/decathlon/android/apptest/common/exception/UnknownErrorException.kt
new file mode 100644
index 0000000..f4ac227
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/exception/UnknownErrorException.kt
@@ -0,0 +1,3 @@
+package com.decathlon.android.apptest.common.exception
+
+class UnknownErrorException : Exception()
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/network/ConnectivityInterceptor.kt b/app/src/main/java/com/decathlon/android/apptest/common/network/ConnectivityInterceptor.kt
new file mode 100644
index 0000000..bdc855a
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/network/ConnectivityInterceptor.kt
@@ -0,0 +1,19 @@
+package com.decathlon.android.apptest.common.network
+
+import android.content.Context
+import com.decathlon.android.apptest.common.exception.NoConnectivityException
+import okhttp3.Interceptor
+import okhttp3.Response
+
+class ConnectivityInterceptor(private val context: Context) : Interceptor {
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ if (!NetworkUtil.isOnline(context)) {
+ throw NoConnectivityException()
+ }
+
+ val builder = chain.request().newBuilder()
+ return chain.proceed(builder.build())
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkConstants.kt b/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkConstants.kt
new file mode 100644
index 0000000..82e5f8a
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkConstants.kt
@@ -0,0 +1,15 @@
+package com.decathlon.android.apptest.common.network
+
+object NetworkConstants {
+
+ const val NETWORK_TIMEOUT_IN_SECONDS: Long = 50
+ const val BASE_URL = "https://api.github.com"
+ const val ACCEPT_HEADER = "Accept"
+ const val REQUEST_GITHUB_V3_API = "application/vnd.github.mercy-preview+json"
+ const val PARAM_PAGE_NUMBER_KEY = "page"
+ const val PARAM_PAGE_NUMBER_VALUE = "1"
+ const val PARAM_PAGE_RESULT_KEY = "per_page"
+ const val PARAM_PAGE_RESULT_VALUE = "10"
+}
+
+
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkUtil.kt b/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkUtil.kt
new file mode 100644
index 0000000..7605cfe
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/network/NetworkUtil.kt
@@ -0,0 +1,17 @@
+package com.decathlon.android.apptest.common.network
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkInfo
+
+class NetworkUtil {
+ companion object {
+ fun isOnline(context: Context): Boolean {
+ val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
+ return if (connectivityManager is ConnectivityManager) {
+ val networkInfo: NetworkInfo? = connectivityManager.activeNetworkInfo
+ networkInfo?.isConnected ?: false
+ } else false
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/common/usecase/SearchGitHubRepositories.kt b/app/src/main/java/com/decathlon/android/apptest/common/usecase/SearchGitHubRepositories.kt
new file mode 100644
index 0000000..732e8ee
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/common/usecase/SearchGitHubRepositories.kt
@@ -0,0 +1,13 @@
+package com.decathlon.android.apptest.common.usecase
+
+import com.decathlon.android.apptest.common.base.SingleUseCase
+import com.decathlon.android.apptest.data.entity.search.SearchResult
+import com.decathlon.android.apptest.data.repository.search.SearchRepository
+import io.reactivex.Single
+
+class SearchGitHubRepositories(private val searchRepository: SearchRepository) : SingleUseCase() {
+ override fun buildUseCaseObservable(params: String?): Single {
+ return params?.let { searchRepository.searchOnGithub(it) } as Single
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/ServiceGenerator.kt b/app/src/main/java/com/decathlon/android/apptest/data/ServiceGenerator.kt
new file mode 100644
index 0000000..0f4c8e7
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/ServiceGenerator.kt
@@ -0,0 +1,55 @@
+package com.decathlon.android.apptest.data
+
+import android.content.Context
+import com.decathlon.android.apptest.BuildConfig
+import com.decathlon.android.apptest.common.network.ConnectivityInterceptor
+import com.decathlon.android.apptest.common.network.NetworkConstants
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import java.util.concurrent.TimeUnit
+
+object ServiceGenerator {
+ private val httpLoggingInterceptor: HttpLoggingInterceptor
+ private val moshi: Moshi
+
+ init {
+ moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+ httpLoggingInterceptor = HttpLoggingInterceptor()
+ httpLoggingInterceptor.level =
+ if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
+ }
+
+ fun createService(serviceClass: Class, context: Context): S {
+ val okHttpClient = OkHttpClient()
+ .newBuilder()
+ .addInterceptor {
+ val originalRequest = it.request()
+ val builder = originalRequest
+ .newBuilder()
+ .addHeader(NetworkConstants.ACCEPT_HEADER, NetworkConstants.REQUEST_GITHUB_V3_API)
+ val newRequest = builder.build()
+ it.proceed(newRequest)
+ }
+ .addInterceptor(httpLoggingInterceptor)
+ .addInterceptor(ConnectivityInterceptor(context))
+ .connectTimeout(NetworkConstants.NETWORK_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
+ .writeTimeout(NetworkConstants.NETWORK_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
+ .readTimeout(NetworkConstants.NETWORK_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
+ .build()
+
+ val retrofit = Retrofit.Builder()
+ .baseUrl(NetworkConstants.BASE_URL)
+ .client(okHttpClient)
+ .addConverterFactory(MoshiConverterFactory.create(moshi))
+ .build()
+
+ return retrofit.create(serviceClass)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/entity/search/License.kt b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/License.kt
new file mode 100644
index 0000000..69f24dc
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/License.kt
@@ -0,0 +1,21 @@
+package com.decathlon.android.apptest.data.entity.search
+
+import com.squareup.moshi.Json
+
+data class License(
+
+ @Json(name = "name")
+ val name: String? = null,
+
+ @Json(name = "spdx_id")
+ val spdxId: String? = null,
+
+ @Json(name = "key")
+ val key: String? = null,
+
+ @Json(name = "url")
+ val url: String? = null,
+
+ @Json(name = "node_id")
+ val nodeId: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Owner.kt b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Owner.kt
new file mode 100644
index 0000000..11bb683
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Owner.kt
@@ -0,0 +1,60 @@
+package com.decathlon.android.apptest.data.entity.search
+
+import com.squareup.moshi.Json
+
+data class Owner(
+
+ @Json(name = "gists_url")
+ val gistsUrl: String? = null,
+
+ @Json(name = "repos_url")
+ val reposUrl: String? = null,
+
+ @Json(name = "following_url")
+ val followingUrl: String? = null,
+
+ @Json(name = "starred_url")
+ val starredUrl: String? = null,
+
+ @Json(name = "login")
+ val login: String? = null,
+
+ @Json(name = "followers_url")
+ val followersUrl: String? = null,
+
+ @Json(name = "type")
+ val type: String? = null,
+
+ @Json(name = "url")
+ val url: String? = null,
+
+ @Json(name = "subscriptions_url")
+ val subscriptionsUrl: String? = null,
+
+ @Json(name = "received_events_url")
+ val receivedEventsUrl: String? = null,
+
+ @Json(name = "avatar_url")
+ val avatarUrl: String? = null,
+
+ @Json(name = "events_url")
+ val eventsUrl: String? = null,
+
+ @Json(name = "html_url")
+ val htmlUrl: String? = null,
+
+ @Json(name = "site_admin")
+ val siteAdmin: Boolean? = null,
+
+ @Json(name = "id")
+ val id: Int? = null,
+
+ @Json(name = "gravatar_id")
+ val gravatarId: String? = null,
+
+ @Json(name = "node_id")
+ val nodeId: String? = null,
+
+ @Json(name = "organizations_url")
+ val organizationsUrl: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Repository.kt b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Repository.kt
new file mode 100644
index 0000000..dd84994
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/Repository.kt
@@ -0,0 +1,231 @@
+package com.decathlon.android.apptest.data.entity.search
+
+import com.squareup.moshi.Json
+
+data class Repository(
+
+ @Json(name = "stargazers_count")
+ val stargazersCount: Int? = null,
+
+ @Json(name = "pushed_at")
+ val pushedAt: String? = null,
+
+ @Json(name = "subscription_url")
+ val subscriptionUrl: String? = null,
+
+ @Json(name = "language")
+ val language: String? = null,
+
+ @Json(name = "branches_url")
+ val branchesUrl: String? = null,
+
+ @Json(name = "issue_comment_url")
+ val issueCommentUrl: String? = null,
+
+ @Json(name = "labels_url")
+ val labelsUrl: String? = null,
+
+ @Json(name = "score")
+ val score: Double? = null,
+
+ @Json(name = "subscribers_url")
+ val subscribersUrl: String? = null,
+
+ @Json(name = "releases_url")
+ val releasesUrl: String? = null,
+
+ @Json(name = "svn_url")
+ val svnUrl: String? = null,
+
+ @Json(name = "id")
+ val id: Int? = null,
+
+ @Json(name = "forks")
+ val forks: Int? = null,
+
+ @Json(name = "archive_url")
+ val archiveUrl: String? = null,
+
+ @Json(name = "git_refs_url")
+ val gitRefsUrl: String? = null,
+
+ @Json(name = "forks_url")
+ val forksUrl: String? = null,
+
+ @Json(name = "statuses_url")
+ val statusesUrl: String? = null,
+
+ @Json(name = "ssh_url")
+ val sshUrl: String? = null,
+
+ @Json(name = "license")
+ val license: License? = null,
+
+ @Json(name = "full_name")
+ val fullName: String? = null,
+
+ @Json(name = "size")
+ val size: Int? = null,
+
+ @Json(name = "languages_url")
+ val languagesUrl: String? = null,
+
+ @Json(name = "html_url")
+ val htmlUrl: String? = null,
+
+ @Json(name = "collaborators_url")
+ val collaboratorsUrl: String? = null,
+
+ @Json(name = "clone_url")
+ val cloneUrl: String? = null,
+
+ @Json(name = "name")
+ val name: String? = null,
+
+ @Json(name = "pulls_url")
+ val pullsUrl: String? = null,
+
+ @Json(name = "default_branch")
+ val defaultBranch: String? = null,
+
+ @Json(name = "hooks_url")
+ val hooksUrl: String? = null,
+
+ @Json(name = "trees_url")
+ val treesUrl: String? = null,
+
+ @Json(name = "tags_url")
+ val tagsUrl: String? = null,
+
+ @Json(name = "private")
+ val jsonMemberPrivate: Boolean? = null,
+
+ @Json(name = "contributors_url")
+ val contributorsUrl: String? = null,
+
+ @Json(name = "has_downloads")
+ val hasDownloads: Boolean? = null,
+
+ @Json(name = "notifications_url")
+ val notificationsUrl: String? = null,
+
+ @Json(name = "open_issues_count")
+ val openIssuesCount: Int? = null,
+
+ @Json(name = "description")
+ val description: String? = null,
+
+ @Json(name = "created_at")
+ val createdAt: String? = null,
+
+ @Json(name = "watchers")
+ val watchers: Int? = null,
+
+ @Json(name = "keys_url")
+ val keysUrl: String? = null,
+
+ @Json(name = "deployments_url")
+ val deploymentsUrl: String? = null,
+
+ @Json(name = "has_projects")
+ val hasProjects: Boolean? = null,
+
+ @Json(name = "archived")
+ val archived: Boolean? = null,
+
+ @Json(name = "has_wiki")
+ val hasWiki: Boolean? = null,
+
+ @Json(name = "updated_at")
+ val updatedAt: String? = null,
+
+ @Json(name = "comments_url")
+ val commentsUrl: String? = null,
+
+ @Json(name = "stargazers_url")
+ val stargazersUrl: String? = null,
+
+ @Json(name = "disabled")
+ val disabled: Boolean? = null,
+
+ @Json(name = "git_url")
+ val gitUrl: String? = null,
+
+ @Json(name = "has_pages")
+ val hasPages: Boolean? = null,
+
+ @Json(name = "owner")
+ val owner: Owner? = null,
+
+ @Json(name = "commits_url")
+ val commitsUrl: String? = null,
+
+ @Json(name = "compare_url")
+ val compareUrl: String? = null,
+
+ @Json(name = "git_commits_url")
+ val gitCommitsUrl: String? = null,
+
+ @Json(name = "topics")
+ val topics: List? = null,
+
+ @Json(name = "blobs_url")
+ val blobsUrl: String? = null,
+
+ @Json(name = "git_tags_url")
+ val gitTagsUrl: String? = null,
+
+ @Json(name = "merges_url")
+ val mergesUrl: String? = null,
+
+ @Json(name = "downloads_url")
+ val downloadsUrl: String? = null,
+
+ @Json(name = "has_issues")
+ val hasIssues: Boolean? = null,
+
+ @Json(name = "url")
+ val url: String? = null,
+
+ @Json(name = "contents_url")
+ val contentsUrl: String? = null,
+
+ @Json(name = "mirror_url")
+ val mirrorUrl: Any? = null,
+
+ @Json(name = "milestones_url")
+ val milestonesUrl: String? = null,
+
+ @Json(name = "teams_url")
+ val teamsUrl: String? = null,
+
+ @Json(name = "fork")
+ val fork: Boolean? = null,
+
+ @Json(name = "issues_url")
+ val issuesUrl: String? = null,
+
+ @Json(name = "events_url")
+ val eventsUrl: String? = null,
+
+ @Json(name = "issue_events_url")
+ val issueEventsUrl: String? = null,
+
+ @Json(name = "assignees_url")
+ val assigneesUrl: String? = null,
+
+ @Json(name = "open_issues")
+ val openIssues: Int? = null,
+
+ @Json(name = "watchers_count")
+ val watchersCount: Int? = null,
+
+ @Json(name = "node_id")
+ val nodeId: String? = null,
+
+ @Json(name = "homepage")
+ val homepage: String? = null,
+
+ @Json(name = "forks_count")
+ val forksCount: Int? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/entity/search/SearchResult.kt b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/SearchResult.kt
new file mode 100644
index 0000000..26f2d20
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/entity/search/SearchResult.kt
@@ -0,0 +1,15 @@
+package com.decathlon.android.apptest.data.entity.search
+
+import com.squareup.moshi.Json
+
+data class SearchResult(
+
+ @Json(name = "total_count")
+ val totalCount: Int? = null,
+
+ @Json(name = "incomplete_results")
+ val incompleteResults: Boolean? = null,
+
+ @Json(name = "items")
+ val items: List? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/repository/search/RemoteSearchRepository.kt b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/RemoteSearchRepository.kt
new file mode 100644
index 0000000..d798c77
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/RemoteSearchRepository.kt
@@ -0,0 +1,51 @@
+package com.decathlon.android.apptest.data.repository.search
+
+import android.content.Context
+import com.decathlon.android.apptest.common.exception.EmptyBodyException
+import com.decathlon.android.apptest.common.exception.ForbiddenException
+import com.decathlon.android.apptest.common.exception.UnknownErrorException
+import com.decathlon.android.apptest.common.network.NetworkConstants
+import com.decathlon.android.apptest.data.ServiceGenerator
+import com.decathlon.android.apptest.data.entity.search.SearchResult
+import io.reactivex.Single
+import java.security.InvalidParameterException
+import java.util.*
+
+class RemoteSearchRepository(private val context: Context?) : SearchRepository {
+ override fun searchOnGithub(query: String): Single {
+ return Single.create {
+
+ val additionalParams = HashMap()
+ additionalParams[NetworkConstants.PARAM_PAGE_NUMBER_KEY] = NetworkConstants.PARAM_PAGE_NUMBER_VALUE
+ additionalParams[NetworkConstants.PARAM_PAGE_RESULT_KEY] = NetworkConstants.PARAM_PAGE_RESULT_VALUE
+
+ if (context == null) {
+ return@create it.onError(InvalidParameterException())
+ }
+
+ val searchService = ServiceGenerator.createService(SearchService::class.java, context)
+ val searchRepositories = searchService.searchRepositories(query, additionalParams)
+
+ try {
+ val response = searchRepositories.execute()
+ when (response.code()) {
+ in 200..300 -> {
+ val searchResult = response.body()
+ if (searchResult != null) {
+ return@create it.onSuccess(searchResult)
+ } else {
+ return@create it.onError(EmptyBodyException())
+ }
+ }
+ 403 -> {
+ return@create it.onError(ForbiddenException())
+ }
+ }
+ it.onError(UnknownErrorException())
+ } catch (e: Exception) {
+ it.onError(e)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchRepository.kt b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchRepository.kt
new file mode 100644
index 0000000..4ca92f1
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchRepository.kt
@@ -0,0 +1,8 @@
+package com.decathlon.android.apptest.data.repository.search
+
+import com.decathlon.android.apptest.data.entity.search.SearchResult
+import io.reactivex.Single
+
+interface SearchRepository {
+ fun searchOnGithub(query: String): Single
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchService.kt b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchService.kt
new file mode 100644
index 0000000..98347fb
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/data/repository/search/SearchService.kt
@@ -0,0 +1,17 @@
+package com.decathlon.android.apptest.data.repository.search
+
+import com.decathlon.android.apptest.data.entity.search.SearchResult
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Query
+import retrofit2.http.QueryMap
+
+interface SearchService {
+ @GET("/search/repositories")
+ fun searchRepositories(
+ @Query(
+ "q",
+ encoded = true
+ ) query: String, @QueryMap additionalParams: Map
+ ): Call
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/GithubSearchActivity.kt b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchActivity.kt
similarity index 50%
rename from app/src/main/java/com/decathlon/android/apptest/GithubSearchActivity.kt
rename to app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchActivity.kt
index f0a6525..beee47f 100644
--- a/app/src/main/java/com/decathlon/android/apptest/GithubSearchActivity.kt
+++ b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchActivity.kt
@@ -1,12 +1,18 @@
-package com.decathlon.android.apptest
+package com.decathlon.android.apptest.githubsearch
-import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.decathlon.android.apptest.R
class GithubSearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
+
+ supportFragmentManager
+ .beginTransaction()
+ .add(R.id.framelayout_githubsearch_fragmentcontainer, GithubSearchFragment.newInstance())
+ .commit()
}
}
diff --git a/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchAdapter.kt b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchAdapter.kt
new file mode 100644
index 0000000..6af90fe
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchAdapter.kt
@@ -0,0 +1,45 @@
+package com.decathlon.android.apptest.githubsearch
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.decathlon.android.apptest.R
+import com.decathlon.android.apptest.data.entity.search.Repository
+import kotlinx.android.synthetic.main.item_search_repository.view.*
+
+class GithubSearchAdapter : RecyclerView.Adapter() {
+
+ var references: List? = emptyList()
+ set(value) {
+ field = value
+ this.notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepositoryViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.item_search_repository, parent, false)
+ return RepositoryViewHolder(view)
+ }
+
+ override fun getItemCount(): Int {
+ return references?.size as Int
+ }
+
+ override fun onBindViewHolder(holder: RepositoryViewHolder, position: Int) {
+ holder.bind(references?.get(position))
+ }
+
+ class RepositoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ fun bind(reference: Repository?) = with(itemView) {
+ textview_itemsearch_title.text = reference?.fullName
+ textview_itemsearch_desc.text = reference?.description
+ textview_itemsearch_stars.text = reference?.stargazersCount.toString()
+
+ Glide
+ .with(itemView.context)
+ .load(reference?.owner?.avatarUrl)
+ .into(imageview_itemsearch_author)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchContract.kt b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchContract.kt
new file mode 100644
index 0000000..b93b37b
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchContract.kt
@@ -0,0 +1,40 @@
+package com.decathlon.android.apptest.githubsearch
+
+import com.decathlon.android.apptest.common.base.BasePresenter
+import com.decathlon.android.apptest.common.base.BaseView
+import com.decathlon.android.apptest.data.entity.search.Repository
+
+interface GithubSearchContract {
+ interface View : BaseView {
+
+ fun showLoadingView()
+
+ fun hideLoadingView()
+
+ fun displaySearchResult(list: List)
+
+ fun showEmptyViewMessage()
+
+ fun showEmptyInputError()
+
+ fun clearInputError()
+
+ fun showConnectivityIssueMessage()
+
+ fun showTimeoutIssueMessage()
+
+ fun showApiRateLimitMessage()
+
+ fun showUnknownErrorMessage()
+
+ fun closeKeyboard()
+ }
+
+ interface Presenter : BasePresenter {
+ fun searchRepositoriesOnGitHub(query: String, system: System)
+ }
+
+ companion object {
+ enum class System { ANDROID, IOS }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchFragment.kt b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchFragment.kt
new file mode 100644
index 0000000..a9fac74
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchFragment.kt
@@ -0,0 +1,185 @@
+package com.decathlon.android.apptest.githubsearch
+
+
+import android.app.AlertDialog
+import android.content.Context.INPUT_METHOD_SERVICE
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.decathlon.android.apptest.R
+import com.decathlon.android.apptest.common.usecase.SearchGitHubRepositories
+import com.decathlon.android.apptest.data.entity.search.Repository
+import com.decathlon.android.apptest.data.repository.search.RemoteSearchRepository
+import com.jakewharton.rxbinding3.widget.editorActionEvents
+import com.jakewharton.rxbinding3.widget.textChangeEvents
+import com.jakewharton.rxbinding3.widget.textChanges
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import kotlinx.android.synthetic.main.fragment_github_search.*
+import java.util.concurrent.TimeUnit
+
+
+class GithubSearchFragment() : Fragment(), GithubSearchContract.View {
+ override lateinit var presenter: GithubSearchContract.Presenter
+ private lateinit var textViewObserver: Disposable
+ private lateinit var githubSearchAdapter: GithubSearchAdapter
+ private lateinit var choosenOs: GithubSearchContract.Companion.System
+ private var firstLaunch = true
+
+ companion object {
+ fun newInstance(): GithubSearchFragment {
+ return GithubSearchFragment()
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_github_search, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ init()
+
+ radiogroup_search.setOnCheckedChangeListener { group, checkedId ->
+ choosenOs = when (checkedId) {
+ R.id.radiobutton_search_android -> GithubSearchContract.Companion.System.ANDROID
+ R.id.radiobutton_search_ios -> GithubSearchContract.Companion.System.IOS
+ else -> GithubSearchContract.Companion.System.ANDROID
+ }
+ presenter.searchRepositoriesOnGitHub(edittext_search.text.toString(), choosenOs)
+ }
+
+ recyclerview_search.apply {
+ setHasFixedSize(true)
+ layoutManager = LinearLayoutManager(activity)
+ adapter = githubSearchAdapter
+ addItemDecoration(DividerItemDecoration(this@GithubSearchFragment.context, DividerItemDecoration.VERTICAL))
+ }
+
+ edittext_search
+ .setOnEditorActionListener { editText, actionId, event ->
+ if(actionId == EditorInfo.IME_ACTION_SEARCH) {
+ presenter.searchRepositoriesOnGitHub(editText.text.toString(), choosenOs)
+ return@setOnEditorActionListener true
+ }
+ return@setOnEditorActionListener false
+ }
+
+ textViewObserver = edittext_search
+ .textChanges()
+ .debounce(1, TimeUnit.SECONDS)
+ .subscribeOn(AndroidSchedulers.mainThread())
+ .subscribe() {
+ if (firstLaunch) {
+ firstLaunch = false
+ } else {
+ presenter.searchRepositoriesOnGitHub(it.toString(), choosenOs)
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ textViewObserver.dispose()
+ presenter.onDestroy()
+ }
+
+ override fun showLoadingView() {
+ activity?.runOnUiThread {
+ loading_search.visibility = View.VISIBLE
+ recyclerview_search.visibility = View.INVISIBLE
+ textview_search_emptyresult.visibility = View.GONE
+ edittext_search.isEnabled = false
+ }
+ }
+
+ override fun hideLoadingView() {
+ activity?.runOnUiThread {
+ loading_search.visibility = View.GONE
+ recyclerview_search.visibility = View.VISIBLE
+ edittext_search.isEnabled = true
+ }
+ }
+
+ override fun showEmptyViewMessage() {
+ activity?.runOnUiThread {
+ textview_search_emptyresult.visibility = View.VISIBLE
+ recyclerview_search.visibility = View.INVISIBLE
+ }
+ }
+
+ override fun showEmptyInputError() {
+ activity?.runOnUiThread {
+ textinputlayout_search.error = getString(R.string.common_emptyinput_error)
+ }
+ }
+
+ override fun clearInputError() {
+ activity?.runOnUiThread {
+ textinputlayout_search.isErrorEnabled = false
+ }
+ }
+
+ override fun showConnectivityIssueMessage() {
+ showAlertView(getString(R.string.common_connectivityissue))
+ }
+
+ override fun showTimeoutIssueMessage() {
+ showAlertView(getString(R.string.common_timeoutissue))
+ }
+
+ override fun showApiRateLimitMessage() {
+ showAlertView(getString(R.string.common_apirateissue))
+ }
+
+ override fun showUnknownErrorMessage() {
+ showAlertView(getString(R.string.common_unknownissue))
+ }
+
+ override fun closeKeyboard() {
+ activity?.runOnUiThread {
+ val inputManager = activity?.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
+ inputManager.hideSoftInputFromWindow(
+ activity?.currentFocus?.windowToken,
+ InputMethodManager.HIDE_NOT_ALWAYS
+ )
+ }
+
+ }
+
+ override fun displaySearchResult(list: List) {
+ if (list.isEmpty()) {
+ showEmptyViewMessage()
+ } else {
+ this.githubSearchAdapter.references = list
+ }
+ }
+
+ private fun init() {
+ val searchRepository = RemoteSearchRepository(context)
+ val searchGitHubRepositories = SearchGitHubRepositories(searchRepository)
+ choosenOs = GithubSearchContract.Companion.System.ANDROID
+
+ presenter = GithubSearchPresenter(
+ this,
+ searchGitHubRepositories
+ )
+ githubSearchAdapter = GithubSearchAdapter()
+ }
+
+ private fun showAlertView(message: String) {
+ val builder = AlertDialog.Builder(context)
+ builder.setTitle(getString(R.string.common_error))
+ builder.setMessage(message)
+ builder.setPositiveButton(android.R.string.ok) { _, _ -> }
+ builder.show()
+ }
+}
diff --git a/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchPresenter.kt b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchPresenter.kt
new file mode 100644
index 0000000..ba01666
--- /dev/null
+++ b/app/src/main/java/com/decathlon/android/apptest/githubsearch/GithubSearchPresenter.kt
@@ -0,0 +1,70 @@
+package com.decathlon.android.apptest.githubsearch
+
+import com.decathlon.android.apptest.common.exception.EmptyBodyException
+import com.decathlon.android.apptest.common.exception.ForbiddenException
+import com.decathlon.android.apptest.common.exception.NoConnectivityException
+import com.decathlon.android.apptest.common.usecase.SearchGitHubRepositories
+import com.decathlon.android.apptest.data.entity.search.Repository
+import com.decathlon.android.apptest.data.entity.search.SearchResult
+import io.reactivex.observers.DisposableSingleObserver
+import java.net.SocketTimeoutException
+
+class GithubSearchPresenter(
+ private val gitHubSearchView: GithubSearchContract.View,
+ private val searchGitHubRepositories: SearchGitHubRepositories
+) : GithubSearchContract.Presenter {
+
+ var isASearchInProgress : Boolean = false
+
+ init {
+ gitHubSearchView.presenter = this
+ }
+
+ override fun searchRepositoriesOnGitHub(query: String, system: GithubSearchContract.Companion.System) {
+ if (query.isNotEmpty()) {
+ if(!isASearchInProgress) {
+ gitHubSearchView.showLoadingView()
+ gitHubSearchView.clearInputError()
+ gitHubSearchView.closeKeyboard()
+ val queryComplement = when (system) {
+ GithubSearchContract.Companion.System.ANDROID -> ANDROID_APPEND_QUERY
+ GithubSearchContract.Companion.System.IOS -> IOS_APPEND_QUERY
+ }
+ searchGitHubRepositories.execute(SearchGitHubRepositoriesObserver(), "$query$queryComplement")
+ isASearchInProgress = true
+ }
+ } else {
+ gitHubSearchView.showEmptyInputError()
+ }
+ }
+
+ override fun onDestroy() {
+ this.searchGitHubRepositories.dispose()
+ }
+
+ private inner class SearchGitHubRepositoriesObserver : DisposableSingleObserver() {
+ override fun onSuccess(results: SearchResult) {
+ gitHubSearchView.hideLoadingView()
+ isASearchInProgress = false
+ results.items?.filterIsInstance()?.let { gitHubSearchView.displaySearchResult(it) }
+ }
+
+ override fun onError(e: Throwable) {
+ gitHubSearchView.hideLoadingView()
+ isASearchInProgress = false
+ when (e) {
+ is NoConnectivityException -> gitHubSearchView.showConnectivityIssueMessage()
+ is SocketTimeoutException -> gitHubSearchView.showTimeoutIssueMessage()
+ is EmptyBodyException -> gitHubSearchView.showEmptyViewMessage()
+ is ForbiddenException -> gitHubSearchView.showApiRateLimitMessage()
+ else -> gitHubSearchView.showUnknownErrorMessage()
+ }
+ }
+
+ }
+
+ companion object {
+ private const val ANDROID_APPEND_QUERY = "+in:nametopic:android+language:java+language:kotlin"
+ private const val IOS_APPEND_QUERY = "+in:nametopic:ios+language:objectivec+language:swift"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
index a0ad202..c206d43 100644
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -5,70 +5,167 @@
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml
index 8bec371..0fcfdb5 100644
--- a/app/src/main/res/layout/activity_search.xml
+++ b/app/src/main/res/layout/activity_search.xml
@@ -5,12 +5,12 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".GithubSearchActivity">
+ tools:context=".githubsearch.GithubSearchActivity">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_search_repository.xml b/app/src/main/res/layout/item_search_repository.xml
new file mode 100644
index 0000000..c65068d
--- /dev/null
+++ b/app/src/main/res/layout/item_search_repository.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7f483a3..6da5419 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,15 @@
D4AppTest
+ Entrer le nom d\'un repository
+ Image du propriétaire du repository
+ Nombre d\'étoiles du repository
+ Android
+ iOS
+ Aucun résultat ne correspond à votre recherche
+ Erreur
+ Aucun texte renseigné
+ La recherche n\'a pas pu aboutir car vous n\'êtes pas connecté à internet
+ La recherche n\'a pas pu aboutir car le délai d\'attente est dépassé
+ La recherche n\'a pas pu aboutir car trop d\'appels ont été effectués ou la requête a été mal formulée
+ La recherche n\'a pas pu aboutir car une erreur inconnue est survenue
diff --git a/app/src/test/java/com/decathlon/android/apptest/ExampleUnitTest.kt b/app/src/test/java/com/decathlon/android/apptest/ExampleUnitTest.kt
index 6564bda..a1492cc 100644
--- a/app/src/test/java/com/decathlon/android/apptest/ExampleUnitTest.kt
+++ b/app/src/test/java/com/decathlon/android/apptest/ExampleUnitTest.kt
@@ -1,9 +1,8 @@
package com.decathlon.android.apptest
+import org.junit.Assert.assertEquals
import org.junit.Test
-import org.junit.Assert.*
-
/**
* Example local unit test, which will execute on the development machine (host).
*
diff --git a/build.gradle b/build.gradle
index af2e406..b222805 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,15 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.21'
+ ext.kotlinVersion = '1.3.31'
+
repositories {
google()
jcenter()
-
+
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -19,10 +20,35 @@ allprojects {
repositories {
google()
jcenter()
-
+
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+
+ext {
+ applicationId = "com.decathlon.android.apptest"
+ minSdkVersion = 21
+ targetSdkVersion = 28
+ compileSdkVersion = 28
+ appVersionCode = 1
+ appVersionName = "1.O"
+ appCompatVersion = "1.0.2"
+ coreKtxVersion = "1.0.2"
+ constraintLayoutVersion = "1.1.3"
+ junitVersion = "4.12"
+ androidTestRunnerVersion = "1.1.1"
+ espressoVersion = "3.1.1"
+ androidxLegacySupportV4Version = "1.0.0"
+ retrofitVersion = "2.5.0"
+ moshiVersion = "1.8.0"
+ okHttpVersion = "3.14.1"
+ rxJavaVersion = "2.2.8"
+ rxAndroidVersion = "2.1.1"
+ recyclerViewVersion = "1.0.0"
+ rxBindingsVersion = "3.0.0-alpha2"
+ glideVersion = "4.9.0"
+ materialVersion = "1.0.0"
+}
\ No newline at end of file