Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 14 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}

android {
Expand All @@ -16,16 +17,13 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
viewBinding = true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -41,21 +39,32 @@ android {
}
}
buildFeatures {
viewBinding true
dataBinding true
}

}

dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'

implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

implementation 'androidx.navigation:navigation-fragment-ktx:2.5.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.2'

implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'

implementation 'com.google.android.gms:play-services-basement:17.3.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
// Coil
implementation "io.coil-kt:coil:2.2.2"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.example.assignment">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -17,7 +19,7 @@
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyBBuSbrTz9SuyFwci1bCzyO0ZLPspoP2os"/>

<activity android:name=".ui.MainActivity"
<activity android:name=".view.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -26,7 +28,7 @@
</intent-filter>
</activity>
<activity
android:name=".ui.MapActivity"
android:name=".view.MapActivity"
android:screenOrientation="portrait">
</activity>
</application>
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/java/com/example/assignment/BindingAdapters.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.example.assignment

import android.provider.Settings.Global.getString
import android.util.Log
import android.widget.TextView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.assignment.adapter.MeteorsListAdapter
import com.example.assignment.model.MeteorData
import java.text.SimpleDateFormat
import java.util.*


/**
* Updates the data shown in the [RecyclerView].
*/
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: MutableList<MeteorData>?) {
val adapter = recyclerView.adapter as MeteorsListAdapter
if (data != null) {
adapter.submitList(data)
Log.d("Binding", data.toString())
adapter.notifyDataSetChanged();
}
}

/**
* Transform mass attribute from Double to String with kg.
*/
@BindingAdapter("app:massText")
fun massText(view: TextView, mass: Double) {
view.text = String.format("%.1f", mass) + R.string.main_mass_unit
}

/**
* Transform date format.
*/
@BindingAdapter("app:dateText")
fun dateText(view: TextView, text: String) {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
val date: Date = format.parse(text)
view.text = date.toString()
}

/**
* Combine reclat and reclong together
*/
@BindingAdapter("app:locationText")
fun locationText(view: TextView, meteor: MeteorData) {
view.text ='[' + meteor.reclat.toString() + ',' + meteor.reclong.toString() + ']'
}

114 changes: 23 additions & 91 deletions app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,124 +1,56 @@
package com.example.assignment.adapter

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.assignment.R
import com.example.assignment.databinding.ItemMeteoriteBinding
import com.example.assignment.model.MeteorData
import com.google.gson.Gson
import java.io.IOException
import java.io.InputStream


class MeteorsListAdapter(
meteorList: List<MeteorData>,
context: Context,
recyclerItemClickListener: RecyclerItemClickListener
) : RecyclerView.Adapter<MeteorsListAdapter.MeteorsViewHolder>() {

private val TAG: String = this.javaClass.name
var meteorDataList: MutableList<MeteorData>
var context: Context
var recyclerItemClickListener: RecyclerItemClickListener

init {
this.context = context
meteorDataList = parseJsonToObject(context) as MutableList<MeteorData>
Log.d(TAG, meteorDataList.toString())
this.recyclerItemClickListener = recyclerItemClickListener
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): MeteorsViewHolder {
Log.d(TAG, "onCreateViewHolder called!!")
val li = LayoutInflater.from(context)
return MeteorsViewHolder(ItemMeteoriteBinding.inflate(li))
}
var meteorDataList: List<MeteorData> = listOf()

override fun onBindViewHolder(
holder: MeteorsViewHolder,
position: Int
) {
Log.d(TAG, "onBindViewHolder bind position:$position")
val meteorDataData: MeteorData = meteorDataList[position]
holder.bind(meteorDataData)
}

override fun getItemCount(): Int {
Log.d(TAG, "MeteorsListAdapter getItemCount=" + meteorDataList.size)
return meteorDataList.size
interface RecyclerItemClickListener {
fun onRecyclerItemClick(meteorItem: MeteorData)
}

private fun remove(item: MeteorData) {
Log.d(TAG, "remove list item:$item")
val pos = meteorDataList.indexOf(item)
if (pos > -1) {
meteorDataList.removeAt(pos)
notifyItemRemoved(pos)
class MeteorsViewHolder private constructor(private var binding: ItemMeteoriteBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(meteorData: MeteorData) {
binding.meteor = meteorData
binding.executePendingBindings()
}
}

private fun getItem(position: Int): MeteorData {
return meteorDataList[position]
}

fun clear() {
Log.d(TAG, "clear Recycler view.")
while (itemCount > 0) {
remove(getItem(0))
companion object {
fun from(parent: ViewGroup): MeteorsViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemMeteoriteBinding.inflate(layoutInflater, parent, false)
return MeteorsViewHolder(binding)
}
}
}

interface RecyclerItemClickListener {
fun onRecyclerItemClick(meteorItem: MeteorData?)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MeteorsViewHolder {
return MeteorsViewHolder.from(parent)
}

inner class MeteorsViewHolder(itemBinding: ItemMeteoriteBinding) :
RecyclerView.ViewHolder(itemBinding.getRoot()) {
var itemMeteoriteBinding: ItemMeteoriteBinding
fun bind(meteorDataData: MeteorData) {
Log.d(TAG, "MeteorsViewHolder bind started")
itemMeteoriteBinding.name.setText(meteorDataData.name)
itemMeteoriteBinding.mass.setText(context.getString(R.string.main_mass, meteorDataData.mass))
itemMeteoriteBinding.location.setText(meteorDataData.reclat.toString() + ", " + meteorDataData.reclong.toString())
itemMeteoriteBinding.date.setText(meteorDataData.year?.substring(0, meteorDataData.year!!.indexOf("-")))
}

init {
itemMeteoriteBinding = itemBinding
itemBinding.getRoot().setOnClickListener(View.OnClickListener {
Log.d(TAG,"MeteorsViewHolder clicked pos=" + meteorDataList[adapterPosition])
recyclerItemClickListener.onRecyclerItemClick(meteorDataList[adapterPosition])
})
}
override fun onBindViewHolder(holder: MeteorsViewHolder, position: Int) {
val meteor: MeteorData = meteorDataList[position]
holder.bind(meteor)
}

private fun loadJSONFromAsset(context: Context): String? {
var json: String? = null
json = try {
val `is`: InputStream = context.assets.open("y77d-th95.json")
val size: Int = `is`.available()
val buffer = ByteArray(size)
`is`.read(buffer)
`is`.close()
String(buffer)
} catch (ex: IOException) {
ex.printStackTrace()
return null
}
return json
override fun getItemCount(): Int {
return meteorDataList.size
}

private fun parseJsonToObject(context: Context): List<MeteorData> {
val jsonString: String? = loadJSONFromAsset(context)
return Gson().fromJson(jsonString, Array<MeteorData>::class.java).toList()
fun submitList(data: MutableList<MeteorData>) {
meteorDataList = data
}



}
17 changes: 9 additions & 8 deletions app/src/main/java/com/example/assignment/model/MeteorData.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.example.assignment.model

import com.squareup.moshi.Json

open class MeteorData(){
var id: String? = null
var mass = 0.0
var name: String? = null
var reclat: String? = null
var reclong: String? = null
var year: String? = null

}
data class MeteorData(
@Json(name = "id") val id: String? = null,
@Json(name = "mass") val mass: Double = 0.0,
@Json(name = "name") val name: String? = null,
@Json(name = "reclat") val reclat: String? = null,
@Json(name = "reclong") val reclong: String? = null,
@Json(name = "year") val year: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.assignment.network

import com.example.assignment.model.MeteorData
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET

private const val BASE_URL = "https://data.nasa.gov/"

/**
* Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
* full Kotlin compatibility.
*/
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()

/**
* Use the Retrofit builder to build a com.example.assignment.network.retrofit object using a Moshi converter with our Moshi
* object.
*/
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()

/**
* A public interface that exposes the [getMeteorsInfo] method
*/
interface MeteorsApiService {
/**
* Returns a [MutableList] of [MeteorData] and this method can be called from a Coroutine.
* The @GET annotation indicates that the "photos" endpoint will be requested with the GET
* HTTP method
*/
@GET("resource/y77d-th95.json?\$where=year>='1900-01-01T00:00:00.000'")//&\$limit=15")
suspend fun getMeteorsInfo(): List<MeteorData>
}

/**
* A public Api object that exposes the lazy-initialized Retrofit service
*/
object MeteorsApi {
val retrofitService: MeteorsApiService by lazy {
retrofit.create(MeteorsApiService::class.java)
}
}
Loading