From 58c21588ce69b0f130faaf13fc64d8f2589b4bb4 Mon Sep 17 00:00:00 2001 From: ankitb Date: Mon, 10 Sep 2018 14:50:15 +0530 Subject: [PATCH] - Created repo list page to fetch the trending git repos - Created RepoListViewModel and RepoRepository to process the API data - Using live data and data binding to display data --- app/.gitignore | 1 + app/build.gradle | 61 +++++++ app/proguard-rules.pro | 21 +++ app/src/main/AndroidManifest.xml | 26 +++ .../com/ankit/trendinggit/TrendingGitApp.kt | 15 ++ .../ankit/trendinggit/model/ApiResponse.kt | 112 ++++++++++++ .../ankit/trendinggit/model/api/ApiClient.kt | 54 ++++++ .../ankit/trendinggit/model/api/ApiService.kt | 12 ++ .../trendinggit/model/api/RepoRepository.kt | 34 ++++ .../view/adapter/RepoListAdapter.kt | 30 +++ .../adapter/viewHolders/RepoListViewHolder.kt | 16 ++ .../trendinggit/view/base/BaseViewModel.kt | 16 ++ .../ankit/trendinggit/view/ui/MainActivity.kt | 20 ++ .../view/ui/repolist/RepoListFragment.kt | 52 ++++++ .../view/ui/repolist/RepoListViewModel.kt | 23 +++ .../ankit/trendinggit/view/utils/Constants.kt | 9 + app/src/main/res/drawable/git_icon.png | Bin 0 -> 8783 bytes app/src/main/res/layout/activity_main.xml | 31 ++++ .../main/res/layout/fragment_repo_list.xml | 38 ++++ .../main/res/layout/view_repo_list_item.xml | 22 +++ app/src/main/res/navigation/nav_graph.xml | 12 ++ app/src/main/res/values/colors.xml | 6 + app/src/main/res/values/dimens.xml | 5 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/styles.xml | 19 ++ build.gradle | 33 ++++ gradle.properties | 19 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ settings.gradle | 1 + 32 files changed, 953 insertions(+) create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/ankit/trendinggit/TrendingGitApp.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/model/ApiResponse.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/model/api/ApiClient.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/model/api/ApiService.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/model/api/RepoRepository.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/adapter/RepoListAdapter.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/adapter/viewHolders/RepoListViewHolder.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/base/BaseViewModel.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/ui/MainActivity.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListFragment.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListViewModel.kt create mode 100644 app/src/main/java/com/ankit/trendinggit/view/utils/Constants.kt create mode 100644 app/src/main/res/drawable/git_icon.png create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/fragment_repo_list.xml create mode 100644 app/src/main/res/layout/view_repo_list_item.xml create mode 100644 app/src/main/res/navigation/nav_graph.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f401ea6 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,61 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'androidx.navigation.safeargs' +apply plugin: 'kotlin-kapt' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.ankit.trendinggit" + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + dataBinding { + enabled = true + } +} + +androidExtensions { + experimental = true +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.0.0-beta01' + implementation 'androidx.constraintlayout:constraintlayout:1.1.2' + + // Navigation component + implementation "android.arch.navigation:navigation-fragment:$rootProject.nav_version" // use -ktx for Kotlin + implementation "android.arch.navigation:navigation-ui:$rootProject.nav_version" // use -ktx for Kotlin + implementation "android.arch.navigation:navigation-runtime-ktx:$rootProject.nav_version" // use -ktx for Kotlin + implementation "android.arch.work:work-runtime-ktx:$rootProject.workVersion" // use -ktx for Kotlin + + // Anko + implementation "org.jetbrains.anko:anko:$rootProject.anko_version" + implementation "org.jetbrains.anko:anko-commons:$rootProject.anko_version" + + // Retrofit + implementation 'com.squareup.retrofit2:retrofit:2.3.0' + implementation 'com.squareup.retrofit2:converter-gson:2.3.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1' + + // Databinding compiler + kapt 'com.android.databinding:compiler:3.2.0-alpha10' +} + +kotlin { + experimental { + coroutines "enable" + } +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4ce38f9 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/TrendingGitApp.kt b/app/src/main/java/com/ankit/trendinggit/TrendingGitApp.kt new file mode 100644 index 0000000..27c3d6e --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/TrendingGitApp.kt @@ -0,0 +1,15 @@ +package com.ankit.trendinggit + +import android.app.Application + +class TrendingGitApp : Application() { + + override fun onCreate() { + super.onCreate() + instance = this + } + + companion object { + lateinit var instance: TrendingGitApp + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/model/ApiResponse.kt b/app/src/main/java/com/ankit/trendinggit/model/ApiResponse.kt new file mode 100644 index 0000000..eeb4df7 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/model/ApiResponse.kt @@ -0,0 +1,112 @@ +package com.ankit.trendinggit.model + +data class GitResponse( + val total_count: Int, + val incomplete_results: Boolean, + val items: List +) + +data class Item( + val id: Int, + val node_id: String, + val name: String, + val full_name: String, + val owner: Owner, + val private: Boolean, + val html_url: String, + val description: String, + val fork: Boolean, + val url: String, + val forks_url: String, + val keys_url: String, + val collaborators_url: String, + val teams_url: String, + val hooks_url: String, + val issue_events_url: String, + val events_url: String, + val assignees_url: String, + val branches_url: String, + val tags_url: String, + val blobs_url: String, + val git_tags_url: String, + val git_refs_url: String, + val trees_url: String, + val statuses_url: String, + val languages_url: String, + val stargazers_url: String, + val contributors_url: String, + val subscribers_url: String, + val subscription_url: String, + val commits_url: String, + val git_commits_url: String, + val comments_url: String, + val issue_comment_url: String, + val contents_url: String, + val compare_url: String, + val merges_url: String, + val archive_url: String, + val downloads_url: String, + val issues_url: String, + val pulls_url: String, + val milestones_url: String, + val notifications_url: String, + val labels_url: String, + val releases_url: String, + val deployments_url: String, + val created_at: String, + val updated_at: String, + val pushed_at: String, + val git_url: String, + val ssh_url: String, + val clone_url: String, + val svn_url: String, + val homepage: String, + val size: Int, + val stargazers_count: Int, + val watchers_count: Int, + val language: String, + val has_issues: Boolean, + val has_projects: Boolean, + val has_downloads: Boolean, + val has_wiki: Boolean, + val has_pages: Boolean, + val forks_count: Int, + val mirror_url: Any, + val archived: Boolean, + val open_issues_count: Int, + val license: License, + val forks: Int, + val open_issues: Int, + val watchers: Int, + val default_branch: String, + val score: Double +) + +data class Owner( + val login: String, + val id: Int, + val node_id: String, + val avatar_url: String, + val gravatar_id: String, + val url: String, + val html_url: String, + val followers_url: String, + val following_url: String, + val gists_url: String, + val starred_url: String, + val subscriptions_url: String, + val organizations_url: String, + val repos_url: String, + val events_url: String, + val received_events_url: String, + val type: String, + val site_admin: Boolean +) + +data class License( + val key: String, + val name: String, + val spdx_id: String, + val url: String, + val node_id: String +) \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/model/api/ApiClient.kt b/app/src/main/java/com/ankit/trendinggit/model/api/ApiClient.kt new file mode 100644 index 0000000..4aaad07 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/model/api/ApiClient.kt @@ -0,0 +1,54 @@ +package com.ankit.trendinggit.model.api + +import com.ankit.trendinggit.view.utils.Constants.Companion.BASE_URL +import com.ankit.trendinggit.view.utils.Constants.Companion.DEBUG +import com.ankit.trendinggit.view.utils.Constants.Companion.REQUEST_TIMEOUT_DURATION +import com.google.gson.GsonBuilder +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object ApiClient { + + val instance: ApiService = Retrofit.Builder().run { + val gson = GsonBuilder() + .enableComplexMapKeySerialization() + .setPrettyPrinting() + .create() + + baseUrl(BASE_URL) + addConverterFactory(GsonConverterFactory.create(gson)) + client(createRequestInterceptorClient()) + build() + }.create(ApiService::class.java) + + + private fun createRequestInterceptorClient(): OkHttpClient { + val interceptor = Interceptor { chain -> + val original = chain.request() + val requestBuilder = original.newBuilder() + val request = requestBuilder.build() + chain.proceed(request) + } + + return if (DEBUG) { + OkHttpClient.Builder() + .addInterceptor(interceptor) + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .connectTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .readTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .writeTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .build() + } else { + OkHttpClient.Builder() + .addInterceptor(interceptor) + .connectTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .readTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .writeTimeout(REQUEST_TIMEOUT_DURATION.toLong(), TimeUnit.SECONDS) + .build() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/model/api/ApiService.kt b/app/src/main/java/com/ankit/trendinggit/model/api/ApiService.kt new file mode 100644 index 0000000..5e61c31 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/model/api/ApiService.kt @@ -0,0 +1,12 @@ +package com.ankit.trendinggit.model.api + +import com.ankit.trendinggit.model.GitResponse +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface ApiService { + + @GET("search/repositories") + fun getRepo(@Query("q") search: String = "trending"): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/model/api/RepoRepository.kt b/app/src/main/java/com/ankit/trendinggit/model/api/RepoRepository.kt new file mode 100644 index 0000000..6a553fc --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/model/api/RepoRepository.kt @@ -0,0 +1,34 @@ +package com.ankit.trendinggit.model.api + +import com.ankit.trendinggit.model.GitResponse +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class RepoRepository { + + // GET repo list + fun getRepoList(onResult: (isSuccess: Boolean, response: GitResponse?) -> Unit) { + + ApiClient.instance.getRepo().enqueue(object :Callback { + override fun onResponse(call: Call?, response: Response?) { + if (response != null && response.isSuccessful) + onResult(true, response.body()!!) + else + onResult(false, null) + } + + override fun onFailure(call: Call?, t: Throwable?) { + onResult(false, null) + } + + }) + } + + companion object { + private var INSTANCE: RepoRepository? = null + fun getInstance() = INSTANCE ?: RepoRepository().also { + INSTANCE = it + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/adapter/RepoListAdapter.kt b/app/src/main/java/com/ankit/trendinggit/view/adapter/RepoListAdapter.kt new file mode 100644 index 0000000..e5a01da --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/adapter/RepoListAdapter.kt @@ -0,0 +1,30 @@ +package com.ankit.trendinggit.view.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.ankit.trendinggit.databinding.ViewRepoListItemBinding +import com.ankit.trendinggit.model.Item +import com.ankit.trendinggit.view.adapter.viewHolders.RepoListViewHolder +import com.ankit.trendinggit.view.ui.repolist.RepoListViewModel + +class RepoListAdapter(private val repoListViewModel: RepoListViewModel) : RecyclerView.Adapter() { + var repoList: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RepoListViewHolder { + val inflater = LayoutInflater.from(parent.context) + val dataBinding = ViewRepoListItemBinding.inflate(inflater, parent, false) + return RepoListViewHolder(dataBinding, repoListViewModel) + } + + override fun getItemCount() = repoList.size + + override fun onBindViewHolder(holder: RepoListViewHolder, position: Int) { + holder.setup(repoList[position]) + } + + fun updateRepoList(repoList: List) { + this.repoList = repoList + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/adapter/viewHolders/RepoListViewHolder.kt b/app/src/main/java/com/ankit/trendinggit/view/adapter/viewHolders/RepoListViewHolder.kt new file mode 100644 index 0000000..af3796e --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/adapter/viewHolders/RepoListViewHolder.kt @@ -0,0 +1,16 @@ +package com.ankit.trendinggit.view.adapter.viewHolders + +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.RecyclerView +import com.ankit.trendinggit.BR +import com.ankit.trendinggit.model.Item +import com.ankit.trendinggit.view.ui.repolist.RepoListViewModel + +class RepoListViewHolder constructor(private val dataBinding: ViewDataBinding, private val repoListViewModel: RepoListViewModel) + : RecyclerView.ViewHolder(dataBinding.root) { + + fun setup(itemData: Item) { + dataBinding.setVariable(BR.itemData, itemData) + dataBinding.executePendingBindings() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/base/BaseViewModel.kt b/app/src/main/java/com/ankit/trendinggit/view/base/BaseViewModel.kt new file mode 100644 index 0000000..c5323c4 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/base/BaseViewModel.kt @@ -0,0 +1,16 @@ +package com.ankit.trendinggit.view.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.ankit.trendinggit.TrendingGitApp + +open class BaseViewModel : ViewModel() { + + val empty = MutableLiveData().apply { value = false } + + val dataLoading = MutableLiveData().apply { value = false } + + val toastMessage = MutableLiveData() + + val appContext get() = TrendingGitApp.instance +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/ui/MainActivity.kt b/app/src/main/java/com/ankit/trendinggit/view/ui/MainActivity.kt new file mode 100644 index 0000000..411c87b --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/ui/MainActivity.kt @@ -0,0 +1,20 @@ +package com.ankit.trendinggit.view.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.findNavController +import androidx.navigation.ui.NavigationUI +import com.ankit.trendinggit.R +import kotlinx.android.synthetic.main.activity_main.* + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + setSupportActionBar(toolbar) + NavigationUI.setupActionBarWithNavController(this, findNavController(R.id.main_nav_fragment)) + } + + override fun onSupportNavigateUp() = findNavController(R.id.main_nav_fragment).navigateUp() +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListFragment.kt b/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListFragment.kt new file mode 100644 index 0000000..c456829 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListFragment.kt @@ -0,0 +1,52 @@ +package com.ankit.trendinggit.view.ui.repolist + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.ankit.trendinggit.databinding.FragmentRepoListBinding +import com.ankit.trendinggit.view.adapter.RepoListAdapter +import kotlinx.android.synthetic.main.fragment_repo_list.* + +class RepoListFragment : Fragment() { + + private lateinit var viewDataBinding: FragmentRepoListBinding + private lateinit var adapter: RepoListAdapter + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + viewDataBinding = FragmentRepoListBinding.inflate(inflater, container, false).apply { + viewmodel = ViewModelProviders.of(this@RepoListFragment).get(RepoListViewModel::class.java) + setLifecycleOwner(viewLifecycleOwner) + } + return viewDataBinding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewDataBinding.viewmodel?.fetchRepoList() + + setupAdapter() + setupObservers() + } + private fun setupObservers() { + viewDataBinding.viewmodel?.repoListLive?.observe(viewLifecycleOwner, Observer { + adapter.updateRepoList(it) + }) + } + + private fun setupAdapter() { + val viewModel = viewDataBinding.viewmodel + if (viewModel != null) { + adapter = RepoListAdapter(viewDataBinding.viewmodel!!) + val layoutManager = LinearLayoutManager(activity) + repo_list_rv.layoutManager = layoutManager + repo_list_rv.addItemDecoration(DividerItemDecoration(activity, layoutManager.orientation)) + repo_list_rv.adapter = adapter + } + } +} diff --git a/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListViewModel.kt b/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListViewModel.kt new file mode 100644 index 0000000..91056e4 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/ui/repolist/RepoListViewModel.kt @@ -0,0 +1,23 @@ +package com.ankit.trendinggit.view.ui.repolist + +import androidx.lifecycle.MutableLiveData +import com.ankit.trendinggit.model.Item +import com.ankit.trendinggit.model.api.RepoRepository +import com.ankit.trendinggit.view.base.BaseViewModel + +class RepoListViewModel : BaseViewModel() { + val repoListLive = MutableLiveData>() + + fun fetchRepoList() { + dataLoading.value = true + RepoRepository.getInstance().getRepoList { isSuccess, response -> + dataLoading.value = false + if(isSuccess) { + repoListLive.value = response?.items + empty.value = false + } else { + empty.value = true + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ankit/trendinggit/view/utils/Constants.kt b/app/src/main/java/com/ankit/trendinggit/view/utils/Constants.kt new file mode 100644 index 0000000..4ec9615 --- /dev/null +++ b/app/src/main/java/com/ankit/trendinggit/view/utils/Constants.kt @@ -0,0 +1,9 @@ +package com.ankit.trendinggit.view.utils + +class Constants { + companion object { + const val BASE_URL = "https://api.github.com/" + const val REQUEST_TIMEOUT_DURATION = 10 + const val DEBUG = true + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/git_icon.png b/app/src/main/res/drawable/git_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a947de8f613202373aab221fa505fe7963fc29b GIT binary patch literal 8783 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..64baac0 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_repo_list.xml b/app/src/main/res/layout/fragment_repo_list.xml new file mode 100644 index 0000000..4693a85 --- /dev/null +++ b/app/src/main/res/layout/fragment_repo_list.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_repo_list_item.xml b/app/src/main/res/layout/view_repo_list_item.xml new file mode 100644 index 0000000..a9ce5f8 --- /dev/null +++ b/app/src/main/res/layout/view_repo_list_item.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..6182538 --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..4e3e6d0 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008000 + #005e00 + #008000 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..5644d09 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + 20dp + + 18sp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..94fa0b2 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Trending Git + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..083c770 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,19 @@ + + + + + + + +