From d879a1bc9b9672bebcfb06c479ea7b788362545b Mon Sep 17 00:00:00 2001 From: Yu Xin Date: Thu, 8 Dec 2022 06:15:05 +0000 Subject: [PATCH 1/5] finishn milestone4 and milestone5 --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 4 +- .../assignment/adapter/MeteorsListAdapter.kt | 53 ++----- .../example/assignment/model/MeteorData.kt | 18 +-- .../assignment/network/MeteorApiService.kt | 31 ++++ .../com/example/assignment/ui/MainActivity.kt | 61 -------- .../example/assignment/view/MainActivity.kt | 136 ++++++++++++++++++ .../assignment/{ui => view}/MapActivity.kt | 2 +- .../assignment/viewModel/MainViewModel.kt | 32 +++++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/activity_map.xml | 2 +- app/src/main/res/layout/view_sort_dialog.xml | 49 +++++++ 12 files changed, 275 insertions(+), 122 deletions(-) create mode 100644 app/src/main/java/com/example/assignment/network/MeteorApiService.kt delete mode 100644 app/src/main/java/com/example/assignment/ui/MainActivity.kt create mode 100644 app/src/main/java/com/example/assignment/view/MainActivity.kt rename app/src/main/java/com/example/assignment/{ui => view}/MapActivity.kt (98%) create mode 100644 app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt create mode 100644 app/src/main/res/layout/view_sort_dialog.xml diff --git a/app/build.gradle b/app/build.gradle index 16d2f0b..814e289 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,13 +41,14 @@ 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.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'com.google.android.material:material:1.7.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' @@ -56,8 +57,10 @@ dependencies { 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' + implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' + implementation 'com.squareup.moshi:moshi-kotlin:1.13.0' + 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' - } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a9f6be1..f94a65e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ android:name="com.google.android.geo.API_KEY" android:value="AIzaSyBBuSbrTz9SuyFwci1bCzyO0ZLPspoP2os"/> - @@ -26,7 +26,7 @@ diff --git a/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt b/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt index b100220..50b93be 100644 --- a/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt +++ b/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt @@ -9,28 +9,25 @@ 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, context: Context, recyclerItemClickListener: RecyclerItemClickListener ) : RecyclerView.Adapter() { private val TAG: String = this.javaClass.name - var meteorDataList: MutableList + var meteorDataList: List var context: Context var recyclerItemClickListener: RecyclerItemClickListener init { this.context = context - meteorDataList = parseJsonToObject(context) as MutableList - Log.d(TAG, meteorDataList.toString()) + meteorDataList = meteorList this.recyclerItemClickListener = recyclerItemClickListener } + override fun onCreateViewHolder( parent: ViewGroup, viewType: Int @@ -54,28 +51,18 @@ class MeteorsListAdapter( return meteorDataList.size } - 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) - } + fun update(data: List) { + meteorDataList = data + notifyDataSetChanged() } private fun getItem(position: Int): MeteorData { return meteorDataList[position] } - fun clear() { - Log.d(TAG, "clear Recycler view.") - while (itemCount > 0) { - remove(getItem(0)) - } - } - interface RecyclerItemClickListener { fun onRecyclerItemClick(meteorItem: MeteorData?) + abstract fun Comparator(function: () -> Int): Comparator } inner class MeteorsViewHolder(itemBinding: ItemMeteoriteBinding) : @@ -97,28 +84,4 @@ class MeteorsListAdapter( }) } } - - 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 - } - - private fun parseJsonToObject(context: Context): List { - val jsonString: String? = loadJSONFromAsset(context) - return Gson().fromJson(jsonString, Array::class.java).toList() - } - - - } \ No newline at end of file diff --git a/app/src/main/java/com/example/assignment/model/MeteorData.kt b/app/src/main/java/com/example/assignment/model/MeteorData.kt index a4ff49c..b369391 100644 --- a/app/src/main/java/com/example/assignment/model/MeteorData.kt +++ b/app/src/main/java/com/example/assignment/model/MeteorData.kt @@ -1,12 +1,12 @@ 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 - -} \ No newline at end of file +data class MeteorData( + @Json(name = "id") var id: String? = null, + @Json(name = "mass") var mass: Double = 0.0, + @Json(name = "name") var name: String? = null, + @Json(name = "reclat") var reclat: String? = null, + @Json(name = "reclong") var reclong: String? = null, + @Json(name = "year") var year: String? = null, +) \ No newline at end of file diff --git a/app/src/main/java/com/example/assignment/network/MeteorApiService.kt b/app/src/main/java/com/example/assignment/network/MeteorApiService.kt new file mode 100644 index 0000000..87580c7 --- /dev/null +++ b/app/src/main/java/com/example/assignment/network/MeteorApiService.kt @@ -0,0 +1,31 @@ +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/" + +private val moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + + +private val retrofit = Retrofit.Builder() + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .baseUrl(BASE_URL) + .build() + +interface MeteorsApiService { + @GET("resource/y77d-th95.json?\$where=year>='1900-01-01T00:00:00.000'")//&\$limit=15") + suspend fun getMeteorsInfo(): List +} + +object MeteorsApi { + val retrofitService: MeteorsApiService by lazy { + retrofit.create(MeteorsApiService::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/assignment/ui/MainActivity.kt b/app/src/main/java/com/example/assignment/ui/MainActivity.kt deleted file mode 100644 index 4b8cd61..0000000 --- a/app/src/main/java/com/example/assignment/ui/MainActivity.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.example.assignment.ui - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.util.Log -import android.view.Menu -import android.view.MenuItem -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.example.assignment.databinding.ActivityMainBinding -import com.example.assignment.R -import com.example.assignment.adapter.MeteorsListAdapter -import com.example.assignment.model.MeteorData -import com.example.assignment.util.Constant - -class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickListener { - private val TAG: String = this.javaClass.name - lateinit var meteorsListAdapter: MeteorsListAdapter - lateinit var activityMainBinding: ActivityMainBinding - lateinit var context: Context - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - this.context = getApplicationContext() - - //get layout view - activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater()) - val view: View = activityMainBinding.getRoot() - setContentView(view) - - val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this) - activityMainBinding.recycler.setLayoutManager(layoutManager) - - fillRecyclerViewData() - - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - getMenuInflater().inflate(R.menu.menu, menu) - return true - } - - - fun fillRecyclerViewData() { - Log.d(TAG, "fillRecyclerViewData called!!") - meteorsListAdapter = MeteorsListAdapter(this,this) - activityMainBinding.recycler.setAdapter(meteorsListAdapter) - } - - override fun onRecyclerItemClick(meteorItem: MeteorData?) { - Log.d(TAG, "onRecyclerItemClick called!!") - val mapActivityIntent = Intent(this, MapActivity::class.java) - mapActivityIntent.putExtra(Constant.Intent.METEORITE_NAME, meteorItem?.name) - mapActivityIntent.putExtra(Constant.Intent.METEORITE_LAT, meteorItem?.reclat) - mapActivityIntent.putExtra(Constant.Intent.METEORITE_LNG, meteorItem?.reclong) - startActivity(mapActivityIntent) - } -} diff --git a/app/src/main/java/com/example/assignment/view/MainActivity.kt b/app/src/main/java/com/example/assignment/view/MainActivity.kt new file mode 100644 index 0000000..5f7b7a9 --- /dev/null +++ b/app/src/main/java/com/example/assignment/view/MainActivity.kt @@ -0,0 +1,136 @@ +package com.example.assignment.view + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.RadioButton +import android.widget.Spinner +import android.widget.Toast +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.example.assignment.adapter.MeteorsListAdapter +import com.example.assignment.databinding.ActivityMainBinding +import com.example.assignment.model.MeteorData +import com.example.assignment.util.* +import com.example.assignment.viewModel.MainViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.util.* + + +class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickListener { + private val TAG: String = this.javaClass.name + private val viewModel: MainViewModel by viewModels() + lateinit var meteorsListAdapter: MeteorsListAdapter + lateinit var activityMainBinding: ActivityMainBinding + lateinit var context: Context + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.context = getApplicationContext() + + //get layout view + activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater()) + val view: View = activityMainBinding.getRoot() + setContentView(view) + + val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this) + activityMainBinding.recycler.setLayoutManager(layoutManager) + + fillRecyclerViewData() + + } + + override fun onResume() { + super.onResume() + viewModel.refresh() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + getMenuInflater().inflate(com.example.assignment.R.menu.menu, menu) + return true + } + + private val renderMuseums = Observer> { + meteorsListAdapter.update(it) + } + + fun fillRecyclerViewData() { + viewModel.meteors.observe(this, renderMuseums) + meteorsListAdapter = MeteorsListAdapter(viewModel.meteors.value ?: emptyList(),this,this) + activityMainBinding.recycler.setAdapter(meteorsListAdapter) + } + + override fun onRecyclerItemClick(meteorItem: MeteorData?) { + val mapActivityIntent = Intent(this, MapActivity::class.java) + mapActivityIntent.putExtra(Constant.Intent.METEORITE_NAME, meteorItem?.name) + mapActivityIntent.putExtra(Constant.Intent.METEORITE_LAT, meteorItem?.reclat) + mapActivityIntent.putExtra(Constant.Intent.METEORITE_LNG, meteorItem?.reclong) + startActivity(mapActivityIntent) + } + + override fun Comparator(function: () -> Int): Comparator { + TODO("Not yet implemented") + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + com.example.assignment.R.id.action_sort -> { + if (viewModel.meteors.value?.isNotEmpty() == true) { + showSortListDialog() + } else { + Toast.makeText(this@MainActivity, com.example.assignment.R.string.toast_no_data, Toast.LENGTH_SHORT).show() + } + } + com.example.assignment.R.id.action_refresh -> { + viewModel.refresh() + Toast.makeText(this, "List refreshed", Toast.LENGTH_SHORT).show() + } + } + return true + } + + private fun showSortListDialog() { + MaterialAlertDialogBuilder(this@MainActivity) + .setTitle("Choose the filtering options:") + .setView(com.example.assignment.R.layout.view_sort_dialog) + .setPositiveButton( + "Apply" + ) { dialogInterface, _ -> applySortListDialog(dialogInterface)} + .setNegativeButton( + "CANCEL" + ) { _, _ -> } + .show() + } + + fun applySortListDialog(dialog: DialogInterface) { + val dialog = Dialog::class.java.cast(dialog) + val fieldSpinner = dialog?.findViewById(com.example.assignment.R.id.sortFieldSpinner) as Spinner + val radioAscendingButton = dialog.findViewById(com.example.assignment.R.id.radioAscending) as RadioButton + val compareText = fieldSpinner.getSelectedItem().toString() + var sortList = listOf() + + if (radioAscendingButton.isChecked) { + when (compareText) { + "Mass" -> sortList = viewModel.meteors.value?.sortedBy { it.mass }?.toList() ?: emptyList() + "Year" -> sortList = viewModel.meteors.value?.sortedBy { it.year }?.toList() ?: emptyList() + "Name" -> sortList = viewModel.meteors.value?.sortedBy { it.name }?.toList() ?: emptyList() + } + } else { + when (compareText) { + "Mass" -> sortList = viewModel.meteors.value?.sortedByDescending { it.mass }?.toList() ?: emptyList() + "Year" -> sortList = viewModel.meteors.value?.sortedByDescending { it.year }?.toList() ?: emptyList() + "Name" -> sortList = viewModel.meteors.value?.sortedByDescending { it.name }?.toList() ?: emptyList() + } + } + meteorsListAdapter.update(sortList) + } +} diff --git a/app/src/main/java/com/example/assignment/ui/MapActivity.kt b/app/src/main/java/com/example/assignment/view/MapActivity.kt similarity index 98% rename from app/src/main/java/com/example/assignment/ui/MapActivity.kt rename to app/src/main/java/com/example/assignment/view/MapActivity.kt index de540f4..b75fb75 100644 --- a/app/src/main/java/com/example/assignment/ui/MapActivity.kt +++ b/app/src/main/java/com/example/assignment/view/MapActivity.kt @@ -1,4 +1,4 @@ -package com.example.assignment.ui +package com.example.assignment.view import android.os.Bundle import android.util.Log diff --git a/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt b/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt new file mode 100644 index 0000000..4339b8d --- /dev/null +++ b/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt @@ -0,0 +1,32 @@ +package com.example.assignment.viewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.assignment.model.MeteorData +import com.example.assignment.network.MeteorsApi +import kotlinx.coroutines.launch + +class MainViewModel : ViewModel() { + private val _meteors = MutableLiveData>() + + val meteors: LiveData> = _meteors + + init { + getMeteorsInfo() + } + + private fun getMeteorsInfo() { + viewModelScope.launch { + try { + _meteors.value = MeteorsApi.retrofitService.getMeteorsInfo() + } catch (e: Exception) { + _meteors.value = listOf() + } + } + } + + fun refresh(){ + getMeteorsInfo() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3f7768f..a28eb2b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".ui.MainActivity"> + tools:context=".view.MainActivity"> + tools:context=".view.MapActivity"> + + + + + + + + + + + + + + \ No newline at end of file From c3b534a17cca05d1a6fe917fd0ee6ffd3d71b3e0 Mon Sep 17 00:00:00 2001 From: Yu Xin Date: Thu, 8 Dec 2022 06:19:21 +0000 Subject: [PATCH 2/5] update README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c13a23 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Yuki's Android Learning Plan guided by Sammy # +## Milestones +1. Bare bones application, including main components and architecture. +* Appropriate choice of architecture. +* Defining components that are needed and how they fit into the architecture. +2. API integration +* Implementation of network layer +3. Displaying list of meteors +* Using appropriate Android components, display a list of meteors received from the API (point 2). +4. List filtering and sorting +* Add interactive filter & sorting UI components to the view +* Display filtered/sorted list +5. Detail view +* More complex view logic by adding a detail view and handling navigation \ No newline at end of file From 00b0814585aa2991be3cf9bfeb9f833672d57e5f Mon Sep 17 00:00:00 2001 From: Yu Xin Date: Wed, 14 Dec 2022 00:39:58 +0000 Subject: [PATCH 3/5] resolve milestone5 comments --- app/build.gradle | 1 + .../assignment/adapter/MeteorsListAdapter.kt | 1 - .../com/example/assignment/util/Constant.kt | 7 ++++ .../example/assignment/view/MainActivity.kt | 38 ++++++++----------- .../assignment/viewModel/MainViewModel.kt | 29 +++++++++++--- app/src/main/res/values/strings.xml | 1 + 6 files changed, 48 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 814e289..5270456 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -59,6 +59,7 @@ dependencies { implementation 'com.google.android.gms:play-services-maps:17.0.0' implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' implementation 'com.squareup.moshi:moshi-kotlin:1.13.0' + implementation 'com.orhanobut:hawk:2.0.1' implementation "io.coil-kt:coil:2.2.2" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt b/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt index 50b93be..9f2e54a 100644 --- a/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt +++ b/app/src/main/java/com/example/assignment/adapter/MeteorsListAdapter.kt @@ -62,7 +62,6 @@ class MeteorsListAdapter( interface RecyclerItemClickListener { fun onRecyclerItemClick(meteorItem: MeteorData?) - abstract fun Comparator(function: () -> Int): Comparator } inner class MeteorsViewHolder(itemBinding: ItemMeteoriteBinding) : diff --git a/app/src/main/java/com/example/assignment/util/Constant.kt b/app/src/main/java/com/example/assignment/util/Constant.kt index ee38f1f..11b6fce 100644 --- a/app/src/main/java/com/example/assignment/util/Constant.kt +++ b/app/src/main/java/com/example/assignment/util/Constant.kt @@ -10,4 +10,11 @@ class Constant { object Map { const val CAMERA_ZOOM = 4.0f } + + object Settings { + const val SORT_FIELD = "sort_field" + const val SORT_ORDER = "sort_orientation" + } + + enum class SortOrder { ASCENDING, DESCENDING } } diff --git a/app/src/main/java/com/example/assignment/view/MainActivity.kt b/app/src/main/java/com/example/assignment/view/MainActivity.kt index 5f7b7a9..6e0da26 100644 --- a/app/src/main/java/com/example/assignment/view/MainActivity.kt +++ b/app/src/main/java/com/example/assignment/view/MainActivity.kt @@ -16,12 +16,14 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.example.assignment.R import com.example.assignment.adapter.MeteorsListAdapter import com.example.assignment.databinding.ActivityMainBinding import com.example.assignment.model.MeteorData import com.example.assignment.util.* import com.example.assignment.viewModel.MainViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.orhanobut.hawk.Hawk import java.util.* @@ -45,13 +47,10 @@ class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickLi val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this) activityMainBinding.recycler.setLayoutManager(layoutManager) - fillRecyclerViewData() + lifecycle.addObserver(viewModel) - } + fillRecyclerViewData() - override fun onResume() { - super.onResume() - viewModel.refresh() } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -77,10 +76,6 @@ class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickLi startActivity(mapActivityIntent) } - override fun Comparator(function: () -> Int): Comparator { - TODO("Not yet implemented") - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { com.example.assignment.R.id.action_sort -> { @@ -98,10 +93,15 @@ class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickLi return true } + override fun onDestroy() { + super.onDestroy() + lifecycle.removeObserver(viewModel) + } + private fun showSortListDialog() { MaterialAlertDialogBuilder(this@MainActivity) - .setTitle("Choose the filtering options:") - .setView(com.example.assignment.R.layout.view_sort_dialog) + .setTitle(R.string.main_dialogue_title) + .setView(R.layout.view_sort_dialog) .setPositiveButton( "Apply" ) { dialogInterface, _ -> applySortListDialog(dialogInterface)} @@ -116,21 +116,15 @@ class MainActivity : AppCompatActivity(), MeteorsListAdapter.RecyclerItemClickLi val fieldSpinner = dialog?.findViewById(com.example.assignment.R.id.sortFieldSpinner) as Spinner val radioAscendingButton = dialog.findViewById(com.example.assignment.R.id.radioAscending) as RadioButton val compareText = fieldSpinner.getSelectedItem().toString() - var sortList = listOf() + Hawk.init(this.context).build() + Hawk.put(Constant.Settings.SORT_FIELD, compareText) if (radioAscendingButton.isChecked) { - when (compareText) { - "Mass" -> sortList = viewModel.meteors.value?.sortedBy { it.mass }?.toList() ?: emptyList() - "Year" -> sortList = viewModel.meteors.value?.sortedBy { it.year }?.toList() ?: emptyList() - "Name" -> sortList = viewModel.meteors.value?.sortedBy { it.name }?.toList() ?: emptyList() - } + Hawk.put(Constant.Settings.SORT_ORDER, Constant.SortOrder.ASCENDING) } else { - when (compareText) { - "Mass" -> sortList = viewModel.meteors.value?.sortedByDescending { it.mass }?.toList() ?: emptyList() - "Year" -> sortList = viewModel.meteors.value?.sortedByDescending { it.year }?.toList() ?: emptyList() - "Name" -> sortList = viewModel.meteors.value?.sortedByDescending { it.name }?.toList() ?: emptyList() - } + Hawk.put(Constant.Settings.SORT_ORDER, Constant.SortOrder.DESCENDING) } + val sortList = viewModel.sortMeteor() meteorsListAdapter.update(sortList) } } diff --git a/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt b/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt index 4339b8d..6979ab8 100644 --- a/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt +++ b/app/src/main/java/com/example/assignment/viewModel/MainViewModel.kt @@ -1,18 +1,17 @@ package com.example.assignment.viewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* import com.example.assignment.model.MeteorData import com.example.assignment.network.MeteorsApi +import com.example.assignment.util.Constant +import com.orhanobut.hawk.Hawk import kotlinx.coroutines.launch -class MainViewModel : ViewModel() { +class MainViewModel : ViewModel(), DefaultLifecycleObserver { private val _meteors = MutableLiveData>() val meteors: LiveData> = _meteors - init { + override fun onResume(owner: LifecycleOwner) { getMeteorsInfo() } @@ -29,4 +28,22 @@ class MainViewModel : ViewModel() { fun refresh(){ getMeteorsInfo() } + + fun sortMeteor(): List { + var sortList = listOf() + if (Hawk.get(Constant.Settings.SORT_ORDER, Constant.SortOrder.ASCENDING) == Constant.SortOrder.ASCENDING) { + when (Hawk.get(Constant.Settings.SORT_FIELD, "Mass")) { + "Mass" -> sortList = _meteors.value?.sortedBy { it.mass }?.toList() ?: emptyList() + "Year" -> sortList = _meteors.value?.sortedBy { it.year }?.toList() ?: emptyList() + "Name" -> sortList = _meteors.value?.sortedBy { it.name }?.toList() ?: emptyList() + } + } else { + when (Hawk.get(Constant.Settings.SORT_FIELD, "Mass")) { + "Mass" -> sortList = _meteors.value?.sortedByDescending { it.mass }?.toList() ?: emptyList() + "Year" -> sortList = _meteors.value?.sortedByDescending { it.year }?.toList() ?: emptyList() + "Name" -> sortList = _meteors.value?.sortedByDescending { it.name }?.toList() ?: emptyList() + } + } + return sortList + } } \ 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 c42b56c..ef56eee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,7 @@ Cancel Ascending Descending + Choose the filtering options: No data available, sorting is disabled From dda3d6f3a292e52f3cf518b035e2f50cbf987386 Mon Sep 17 00:00:00 2001 From: Yu Xin Date: Wed, 14 Dec 2022 01:38:42 +0000 Subject: [PATCH 4/5] repository with internal storage for data persistence --- .idea/misc.xml | 5 +- README.md | 5 +- app/build.gradle | 22 +++++-- .../assignment/adapter/MeteorsListAdapter.kt | 1 - .../com/example/assignment/data/ApiClient.kt | 44 +++++++++++++ .../assignment/data/MeteorRemoteDataSource.kt | 42 ++++++++++++ .../assignment/data/OperationCallback.kt | 7 ++ .../com/example/assignment/di/injection.kt | 44 +++++++++++++ .../example/assignment/model/MeteorData.kt | 16 ++--- .../assignment/model/MeteorDataSource.kt | 8 +++ .../assignment/model/MeteorRepository.kt | 14 ++++ .../assignment/network/MeteorApiService.kt | 31 --------- .../com/example/assignment/util/Constant.kt | 4 ++ .../com/example/assignment/util/FileHelper.kt | 55 ++++++++++++++++ .../example/assignment/view/MainActivity.kt | 66 +++++++++++++------ .../assignment/viewModel/MainViewModel.kt | 51 +++++++++----- .../assignment/viewModel/ViewModelFactory.kt | 15 +++++ app/src/main/res/drawable/ic_error.xml | 43 ++++++++++++ app/src/main/res/layout/activity_main.xml | 20 ++++++ app/src/main/res/layout/item_meteorite.xml | 3 +- app/src/main/res/layout/layout_empty.xml | 13 ++++ app/src/main/res/layout/layout_error.xml | 24 +++++++ app/src/main/res/values/strings.xml | 7 +- 23 files changed, 451 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/com/example/assignment/data/ApiClient.kt create mode 100644 app/src/main/java/com/example/assignment/data/MeteorRemoteDataSource.kt create mode 100644 app/src/main/java/com/example/assignment/data/OperationCallback.kt create mode 100644 app/src/main/java/com/example/assignment/di/injection.kt create mode 100644 app/src/main/java/com/example/assignment/model/MeteorDataSource.kt create mode 100644 app/src/main/java/com/example/assignment/model/MeteorRepository.kt delete mode 100644 app/src/main/java/com/example/assignment/network/MeteorApiService.kt create mode 100644 app/src/main/java/com/example/assignment/util/FileHelper.kt create mode 100644 app/src/main/java/com/example/assignment/viewModel/ViewModelFactory.kt create mode 100644 app/src/main/res/drawable/ic_error.xml create mode 100644 app/src/main/res/layout/layout_empty.xml create mode 100644 app/src/main/res/layout/layout_error.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 9f0815d..578edc5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,10 +3,13 @@