mirror of
https://github.com/hmalik144/Android-developer-tech-test---POQ.git
synced 2025-12-10 03:05:24 +00:00
New commit with added features:
- filter list - new list item layout
This commit is contained in:
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -8,7 +8,7 @@ android {
|
|||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.h_mal.myapplication"
|
applicationId "com.example.h_mal.myapplication"
|
||||||
minSdkVersion 15
|
minSdkVersion 23
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
@@ -33,4 +33,5 @@ dependencies {
|
|||||||
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
|
implementation 'com.squareup.okhttp3:okhttp:4.2.1'
|
||||||
implementation 'com.google.code.gson:gson:2.8.5'
|
implementation 'com.google.code.gson:gson:2.8.5'
|
||||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
||||||
|
implementation 'com.android.support:cardview-v7:28.0.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,86 @@
|
|||||||
package com.example.h_mal.myapplication
|
package com.example.h_mal.myapplication
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.net.UrlQuerySanitizer
|
||||||
|
import android.opengl.Visibility
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.Filter
|
||||||
import com.example.h_mal.myapplication.model.JsonObject
|
import com.example.h_mal.myapplication.model.JsonObject
|
||||||
import kotlinx.android.synthetic.main.repo_list_item.view.*
|
import kotlinx.android.synthetic.main.repo_list_item.view.*
|
||||||
|
|
||||||
|
//custom list adapter extends from array adater
|
||||||
class ListViewAdapter(context: Context, objects: MutableList<JsonObject>) :
|
class ListViewAdapter(context: Context, objects: MutableList<JsonObject>) :
|
||||||
ArrayAdapter<JsonObject>(context, 0, objects){
|
ArrayAdapter<JsonObject>(context, 0, objects){
|
||||||
|
|
||||||
|
//populate each view
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
//if the current view is null then inflate our list item object into list
|
||||||
var view: View? = convertView
|
var view: View? = convertView
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
view = LayoutInflater.from(context).inflate(R.layout.repo_list_item, parent, false)
|
view = LayoutInflater.from(context).inflate(R.layout.repo_list_item, parent, false)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get the current item from the current position
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
|
|
||||||
view?.name?.text = item?.name
|
//set name and description
|
||||||
view?.description?.text = item?.description
|
view.name?.text = item?.name
|
||||||
|
view.description?.text = item?.description
|
||||||
|
|
||||||
|
//parse date from the date time string and set the date
|
||||||
|
val dateString = item?.date?.split('T')?.get(0)
|
||||||
|
view.date?.text = dateString
|
||||||
|
|
||||||
return view!!
|
//check if the language object is null
|
||||||
|
if (item.language != null){
|
||||||
|
//language exists in object
|
||||||
|
//view holdin the language to be visible
|
||||||
|
view.lang_layout.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
//language text and corresponding colour according to github is applied
|
||||||
|
view.lang?.text = item.language
|
||||||
|
getColor(item.language)?.let { it1 -> view.lang_col.setCardBackgroundColor(it1) }
|
||||||
|
}else{
|
||||||
|
//language was null therefore view to be hidden
|
||||||
|
view.lang_layout.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply on click listener to item
|
||||||
|
view.setOnClickListener{
|
||||||
|
//click item opens then url
|
||||||
|
openLink(item.repoUrlString)
|
||||||
|
}
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
//function for opening the link
|
||||||
|
fun openLink(urlString : String?){
|
||||||
|
//open link to repo if the url is not nu;;
|
||||||
|
if (urlString != null){
|
||||||
|
val openURL = Intent(Intent.ACTION_VIEW)
|
||||||
|
openURL.data = Uri.parse(urlString)
|
||||||
|
context.startActivity(openURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the corresponding colour based on the programming language
|
||||||
|
fun getColor(language: String?) : Int?{
|
||||||
|
return when(language) {
|
||||||
|
"Ruby" -> context.getColor(R.color.Ruby)
|
||||||
|
"Java"-> context.getColor(R.color.Java)
|
||||||
|
"Objective-C" -> context.getColor(R.color.ObjectiveC)
|
||||||
|
"JavaScript" -> context.getColor(R.color.JavaScript)
|
||||||
|
"CSS" -> context.getColor(R.color.CSS)
|
||||||
|
"Shell" -> context.getColor(R.color.Shell)
|
||||||
|
//if the language has no match return nothing
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,10 @@ import android.content.Context
|
|||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.widget.SwipeRefreshLayout
|
import android.support.v4.widget.SwipeRefreshLayout
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.SearchView
|
||||||
import com.example.h_mal.myapplication.model.JsonObject
|
import com.example.h_mal.myapplication.model.JsonObject
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
@@ -12,49 +16,101 @@ import java.io.IOException
|
|||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
lateinit var thecontext: Context
|
lateinit var searchView: SearchView
|
||||||
|
|
||||||
|
val urlString = "https://api.github.com/orgs/square/repos"
|
||||||
|
|
||||||
companion object{
|
|
||||||
val urlString = "https://api.github.com/orgs/square/repos"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
thecontext = this
|
//begin populating list
|
||||||
|
|
||||||
executeTask()
|
executeTask()
|
||||||
|
|
||||||
|
//set a listener for the swipe to refresh
|
||||||
swipe_refresh.setOnRefreshListener(swipeRefreshListener)
|
swipe_refresh.setOnRefreshListener(swipeRefreshListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//implement search interface in the menu
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
//inflate custom menu as our menu
|
||||||
|
menuInflater.inflate(R.menu.menu, menu)
|
||||||
|
//extract searchview
|
||||||
|
val searchItem = menu?.findItem(R.id.search)
|
||||||
|
//set searchview globally
|
||||||
|
searchView = searchItem?.actionView as SearchView
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
val swipeRefreshListener = SwipeRefreshLayout.OnRefreshListener{
|
val swipeRefreshListener = SwipeRefreshLayout.OnRefreshListener{
|
||||||
|
//populate list when pulling to refresh
|
||||||
executeTask()
|
executeTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun executeTask(){
|
fun executeTask(){
|
||||||
|
//clear list before populating
|
||||||
|
list_view.adapter = null
|
||||||
|
|
||||||
|
//create url from url string urlstring
|
||||||
val url = Request.Builder().url(urlString).build()
|
val url = Request.Builder().url(urlString).build()
|
||||||
|
//create a okhttpclient for a get request from url
|
||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
|
|
||||||
|
//call the url and retrieve its callback
|
||||||
client.newCall(url).enqueue(object: Callback {
|
client.newCall(url).enqueue(object: Callback {
|
||||||
|
//failure of retrieval callback
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
//print error to log
|
||||||
System.err.println(e)
|
System.err.println(e)
|
||||||
|
//if swipe refresh is refreshing then stop
|
||||||
swipe_refresh.isRefreshing = false
|
swipe_refresh.isRefreshing = false
|
||||||
|
//list is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//successful retrieval callback
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
//get the JSON from the body
|
||||||
val urlResponse = response.body?.string()
|
val urlResponse = response.body?.string()
|
||||||
|
|
||||||
|
//print the response to the logs
|
||||||
println("response = $urlResponse")
|
println("response = $urlResponse")
|
||||||
|
|
||||||
|
//create gson object
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
|
//create a mutable list of objects extracted from the json text
|
||||||
val objectList = gson.fromJson(urlResponse, Array<JsonObject>::class.java).asList().toMutableList()
|
val objectList = gson.fromJson(urlResponse, Array<JsonObject>::class.java).asList().toMutableList()
|
||||||
|
|
||||||
runOnUiThread{
|
if (objectList.size >0){
|
||||||
list_view.adapter = ListViewAdapter(thecontext, objectList)
|
//update the ui
|
||||||
swipe_refresh.isRefreshing = false
|
runOnUiThread{
|
||||||
|
//custom list view adapter created
|
||||||
|
val adapterLV = ListViewAdapter(baseContext, objectList)
|
||||||
|
//apply adapter to listview
|
||||||
|
list_view.adapter = adapterLV
|
||||||
|
//if swipe refresh is refreshing then stop
|
||||||
|
swipe_refresh.isRefreshing = false
|
||||||
|
|
||||||
|
//search view has its query change listener applied
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
|
||||||
|
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//as the test is changed the list is filtered
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
|
//filter list function
|
||||||
|
adapterLV.filter.filter(newText)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//list is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ data class JsonObject(
|
|||||||
var description : String? = null,
|
var description : String? = null,
|
||||||
@SerializedName("language")
|
@SerializedName("language")
|
||||||
var language : String? = null,
|
var language : String? = null,
|
||||||
|
@SerializedName("created_at")
|
||||||
|
var date : String? = null,
|
||||||
@SerializedName("html_url")
|
@SerializedName("html_url")
|
||||||
var repoUrlString : String? = null
|
var repoUrlString : String? = null
|
||||||
)
|
)
|
||||||
|
|||||||
9
app/src/main/res/drawable/ic_search_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_search_black_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/ic_search_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_search_white_24dp.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/ic_sort_black_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_sort_black_24dp.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -19,4 +19,6 @@
|
|||||||
|
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
</android.support.v4.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
@@ -1,18 +1,83 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_height="match_parent">
|
android:layout_width="match_parent"
|
||||||
<TextView
|
android:layout_height="wrap_content"
|
||||||
android:hint="name"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" tools:layout_editor_absoluteY="16dp"
|
<android.support.v7.widget.CardView
|
||||||
tools:layout_editor_absoluteX="16dp" android:id="@+id/name"/>
|
android:layout_width="match_parent"
|
||||||
<TextView
|
android:layout_height="wrap_content"
|
||||||
android:hint="description"
|
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="12dp"
|
||||||
android:layout_below="@id/name"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_marginRight="12dp"
|
||||||
android:layout_height="wrap_content" tools:layout_editor_absoluteY="44dp"
|
android:layout_marginBottom="6dp"
|
||||||
tools:layout_editor_absoluteX="16dp" android:id="@+id/description"/>
|
app:cardCornerRadius="12dp">
|
||||||
</RelativeLayout>
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="#30ABC5"
|
||||||
|
tools:text="Name of repo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/date"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:text="19/04/2018" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:backgroundTint="#30ABC5"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Multi line description, text and more text then text
|
||||||
|
add more text more of a description. Multi line
|
||||||
|
description, text and more text then text add
|
||||||
|
more text more of a description." />
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/lang_layout" android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp" android:gravity="right">
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="match_parent"
|
||||||
|
tools:text="Java" android:gravity="center" android:id="@+id/lang"/>
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
app:cardCornerRadius="8dp" app:cardBackgroundColor="#E03F3F" android:id="@+id/lang_col"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
7
app/src/main/res/menu/menu.xml
Normal file
7
app/src/main/res/menu/menu.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/search"
|
||||||
|
android:title="@string/search_title"
|
||||||
|
android:icon="@drawable/ic_search_white_24dp"
|
||||||
|
app:showAsAction="collapseActionView|ifRoom" app:actionViewClass="android.widget.SearchView"/>
|
||||||
|
</menu>
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#008577</color>
|
<color name="colorPrimary">#30ABC5</color>
|
||||||
<color name="colorPrimaryDark">#00574B</color>
|
<color name="colorPrimaryDark">#247E91</color>
|
||||||
<color name="colorAccent">#D81B60</color>
|
<color name="colorAccent">#D81B60</color>
|
||||||
|
|
||||||
|
<color name="Ruby">#701516</color>
|
||||||
|
<color name="Java">#b07219</color>
|
||||||
|
<color name="ObjectiveC">#438eff</color>
|
||||||
|
<color name="JavaScript">#f1e05a</color>
|
||||||
|
<color name="CSS">#563d7c</color>
|
||||||
|
<color name="Shell">#89e051</color>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">SquareGithubRepo</string>
|
<string name="app_name">SquareGithubRepo</string>
|
||||||
|
<string name="search_title">Search</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user