Skip to content

Commit 7cf0c07

Browse files
committed
change weather location
1 parent 7ab3075 commit 7cf0c07

File tree

11 files changed

+130
-32
lines changed

11 files changed

+130
-32
lines changed

app/src/main/java/rahulstech/android/weatherapp/activity/CitySearchActivity.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@ package rahulstech.android.weatherapp.activity
33
import android.os.Bundle
44
import android.text.Editable
55
import android.text.TextWatcher
6+
import android.util.Log
7+
import android.view.View
68
import android.widget.EditText
9+
import android.widget.Toast
710
import androidx.appcompat.app.AppCompatActivity
811
import androidx.lifecycle.ViewModelProvider
912
import androidx.recyclerview.widget.LinearLayoutManager
1013
import androidx.recyclerview.widget.RecyclerView
1114
import rahulstech.android.weatherapp.R
1215
import rahulstech.android.weatherapp.adapter.CitySearchResultAdapter
16+
import rahulstech.android.weatherapp.helper.OnRecyclerViewItemClickListener
17+
import rahulstech.android.weatherapp.helper.RecyclerViewItemClickHelper
18+
import rahulstech.android.weatherapp.setting.SettingsStorage
1319
import rahulstech.android.weatherapp.viewmodel.CitySearchViewModel
1420
import rahulstech.weather.repository.City
1521

16-
class CitySearchActivity : AppCompatActivity() {
22+
class CitySearchActivity : AppCompatActivity(), OnRecyclerViewItemClickListener {
1723

1824
private val TAG = CitySearchActivity::class.java.simpleName
1925

@@ -33,10 +39,13 @@ class CitySearchActivity : AppCompatActivity() {
3339
searchResult = findViewById(R.id.search_result)
3440

3541
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
42+
val itemClickHelper = RecyclerViewItemClickHelper(searchResult)
43+
itemClickHelper.addOnRecyclerViewItemClickListener(this)
3644
citySearchResultAdapter = CitySearchResultAdapter(this)
3745

3846
searchResult.layoutManager = layoutManager
3947
searchResult.adapter = citySearchResultAdapter
48+
searchResult.addOnItemTouchListener(itemClickHelper)
4049

4150
viewModel = ViewModelProvider(this).get(CitySearchViewModel::class.java)
4251
viewModel.citySearchResult.observe(this) { onCitiesFetched(it) }
@@ -47,12 +56,28 @@ class CitySearchActivity : AppCompatActivity() {
4756
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
4857

4958
override fun afterTextChanged(s: Editable?) {
50-
viewModel.changeKeyword(s.toString())
59+
val keyword = s.toString()
60+
if (keyword.length < 3 ) {
61+
viewModel.changeKeyword(null)
62+
}
63+
else {
64+
viewModel.changeKeyword(keyword)
65+
}
5166
}
5267
})
5368
}
5469

5570
private fun onCitiesFetched(cities: List<City>) {
5671
citySearchResultAdapter.submitList(cities)
5772
}
73+
74+
override fun onClickItem(recyclerView: RecyclerView, itemView: View, adapterPosition: Int) {
75+
val item = citySearchResultAdapter.currentList[adapterPosition]
76+
val locationId = item.locationId
77+
val location = "${item.name}, ${item.region}, ${item.country}"
78+
Log.i(TAG, "selected location: id=$locationId location= $location")
79+
SettingsStorage.get(this).setWeatherLocationId(locationId)
80+
Toast.makeText(this, getString(R.string.message_current_location_changed, location), Toast.LENGTH_SHORT).show()
81+
finish()
82+
}
5883
}

app/src/main/java/rahulstech/android/weatherapp/activity/HomeActivity.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import rahulstech.android.weatherapp.adapter.HourlyForecastAdapter
1616
import rahulstech.android.weatherapp.getTemperatureCelsiusText
1717
import rahulstech.android.weatherapp.getWeatherConditionIcon
1818
import rahulstech.android.weatherapp.getWeatherConditionText
19+
import rahulstech.android.weatherapp.setting.SettingsStorage
1920
import rahulstech.android.weatherapp.viewmodel.HomeViewModel
2021
import rahulstech.weather.repository.WeatherCondition
2122
import rahulstech.weather.repository.WeatherForecast
@@ -83,12 +84,15 @@ class HomeActivity : AppCompatActivity() {
8384
forecastHourly.layoutManager = layoutManager
8485
forecastHourly.adapter = hourlyForecastAdapter
8586

86-
87-
// Delhi: 1112321
88-
// Kandi: 1118644
8987
viewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
9088
viewModel.weatherToday.observe(this) { onCurrentWeatherReportFetched(it) }
91-
viewModel.setLocationId("1112321")
89+
}
90+
91+
override fun onStart() {
92+
super.onStart()
93+
94+
val weatherLocationId = SettingsStorage.get(this).getWeatherLocationId()
95+
viewModel.setLocationId(weatherLocationId)
9296
}
9397

9498
private fun onCurrentWeatherReportFetched(report: WeatherForecast?) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package rahulstech.android.weatherapp.helper
2+
3+
import android.view.GestureDetector
4+
import android.view.MotionEvent
5+
import android.view.View
6+
import androidx.recyclerview.widget.RecyclerView
7+
import java.lang.ref.WeakReference
8+
9+
interface OnRecyclerViewItemClickListener {
10+
11+
fun onClickItem(recyclerView: RecyclerView, itemView: View, adapterPosition: Int)
12+
}
13+
14+
class RecyclerViewItemClickHelper(private val recyclerView: RecyclerView) : RecyclerView.SimpleOnItemTouchListener() {
15+
16+
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
17+
18+
override fun onSingleTapConfirmed(e: MotionEvent): Boolean = handleClick(e)
19+
}
20+
21+
private val gestureDetector = GestureDetector(recyclerView.context, gestureListener)
22+
23+
private var clickListenerRef: WeakReference<OnRecyclerViewItemClickListener> = WeakReference<OnRecyclerViewItemClickListener>(null)
24+
25+
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean = gestureDetector.onTouchEvent(e)
26+
27+
fun addOnRecyclerViewItemClickListener(listener: OnRecyclerViewItemClickListener) {
28+
clickListenerRef = WeakReference<OnRecyclerViewItemClickListener>(listener)
29+
}
30+
31+
private fun handleClick(e: MotionEvent): Boolean {
32+
val listener = clickListenerRef.get() ?: return false
33+
val itemView: View = getItemViewUnderTouch(recyclerView, e) ?: return false
34+
val adapterPosition = recyclerView.getChildAdapterPosition(itemView)
35+
listener.onClickItem(recyclerView, itemView, adapterPosition)
36+
return true
37+
}
38+
39+
private fun getItemViewUnderTouch(recyclerView: RecyclerView, e: MotionEvent): View? {
40+
val touchX = e.x
41+
val touchY = e.y
42+
return recyclerView.findChildViewUnder(touchX, touchY)
43+
}
44+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package rahulstech.android.weatherapp.setting
2+
3+
import android.content.Context
4+
import androidx.core.content.edit
5+
6+
class SettingsStorage private constructor(private val context: Context) {
7+
8+
private val SHARED_PREFERENCE_NAME = "name.shared_preferences.weather"
9+
10+
private val KEY_WEATHER_LOCATION_ID = "weather_location_id"
11+
12+
private val DEFAULT_LOCATION_ID = "2801268"
13+
14+
private val sharedPreferences by lazy { context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) }
15+
16+
private fun readString(key: String, defaultValue: String? = null): String? =
17+
sharedPreferences.getString(key, defaultValue)
18+
19+
private fun writeString(key: String, value: String?) =
20+
sharedPreferences.edit(true) { putString(key, value) }
21+
22+
fun setWeatherLocationId(locationId: String) {
23+
writeString(KEY_WEATHER_LOCATION_ID, locationId)
24+
}
25+
26+
fun getWeatherLocationId(): String {
27+
return readString(KEY_WEATHER_LOCATION_ID, DEFAULT_LOCATION_ID) as String
28+
}
29+
30+
companion object {
31+
32+
fun get(context: Context) = SettingsStorage(context)
33+
}
34+
}

app/src/main/java/rahulstech/android/weatherapp/viewmodel/CitySearchViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ class CitySearchViewModel(app: Application) : AndroidViewModel(app) {
1313

1414
private val repo by lazy { WeatherRepository(BuildConfig.WEATHER_API_KEY) }
1515

16-
private val keywordLiveData = MutableLiveData<String>()
16+
private val keywordLiveData = MutableLiveData<String?>()
1717

1818
val citySearchResult: LiveData<List<City>> by lazy { keywordLiveData.switchMap { repo.searchCity(it) } }
1919

20-
fun changeKeyword(keyword: String) {
20+
fun changeKeyword(keyword: String?) {
2121
keywordLiveData.value = keyword
2222
}
2323
}

app/src/main/res/layout/item_city_search_result.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
33
android:layout_width="match_parent"
44
android:layout_height="wrap_content"
5+
xmlns:app="http://schemas.android.com/apk/res-auto"
56
android:orientation="vertical"
67
android:gravity="start"
7-
android:padding="8dp">
8+
android:background="?attr/selectableItemBackground"
9+
android:clickable="true">
810

911
<TextView
1012
android:id="@+id/label_city"
1113
android:layout_width="match_parent"
1214
android:layout_height="wrap_content"
13-
android:layout_margin="4dp"
15+
android:layout_margin="8dp"
1416
android:textAppearance="@style/TextAppearance.AppCompat.Headline" />
1517

1618
<TextView
@@ -20,4 +22,9 @@
2022
android:layout_margin="4dp"
2123
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
2224

25+
<com.google.android.material.divider.MaterialDivider
26+
android:layout_width="match_parent"
27+
android:layout_height="2dp"
28+
app:lastItemDecorated="false" />
29+
2330
</LinearLayout>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
<string name="text_blizzard">Blizzard</string>
2525
<string name="text_search_city">Search city</string>
2626
<string name="text_load_more_cities">Load more cities...</string>
27+
<string name="message_current_location_changed">location changed to %s</string>
2728
</resources>

backend/weather-api/src/main/java/com/weather/api/WeatherApi.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,12 @@ import retrofit2.http.Query
66

77
interface WeatherApi {
88

9-
@GET("astronomy.json")
10-
suspend fun getAstronomyForDate(
11-
@Query("q") q: String,
12-
@Query("dt") dt: String
13-
): Response<Astronomy>
14-
159
@GET("forecast.json")
1610
suspend fun getForecast(
1711
@Query("q") q: String,
1812
@Query("days") days: Int
1913
): Response<Forecast>
2014

21-
22-
@GET("timezone.json")
23-
fun getTimezone(
24-
@Query("q") q: String
25-
): Response<Timezone>
26-
2715
@GET("search.json")
2816
suspend fun searchCity(
2917
@Query("q") q: String

backend/weather-repository/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ dependencies {
3535

3636
implementation libs.androidx.core.ktx
3737
implementation libs.androidx.appcompat
38+
implementation libs.androidx.lifecycle.viewmode.ktx
3839
}

backend/weather-repository/src/main/java/rahulstech/weather/repository/WeatherRepository.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package rahulstech.weather.repository
33
import android.util.Log
44
import androidx.lifecycle.LiveData
55
import androidx.lifecycle.MutableLiveData
6+
import androidx.lifecycle.asLiveData
67
import com.weather.api.Location
78
import com.weather.api.WeatherClient
89
import kotlinx.coroutines.Dispatchers
@@ -30,7 +31,6 @@ class WeatherRepository(private val apiKey: String) {
3031
private val api by lazy { WeatherClient.getInstance(apiKey).api }
3132

3233
fun getWeatherToday(locationId: String): LiveData<WeatherForecast?> {
33-
val result = MutableLiveData<WeatherForecast?>()
3434
val mainFlow: Flow<WeatherForecast?> = flow {
3535
val response = api.getForecast("id:$locationId", 1)
3636
if (response.isSuccessful) {
@@ -81,13 +81,7 @@ class WeatherRepository(private val apiKey: String) {
8181
Log.e(TAG, null,it)
8282
}
8383

84-
GlobalScope.launch {
85-
mainFlow.collect {
86-
result.postValue(it)
87-
}
88-
}
89-
90-
return result
84+
return mainFlow.asLiveData()
9185
}
9286

9387
fun searchCity(keyword: String?): LiveData<List<City>> {
@@ -130,7 +124,7 @@ class WeatherRepository(private val apiKey: String) {
130124
}
131125

132126
private fun convertLocationToCity(locationId: String, location: Location): City {
133-
val zoneId = if (location.tz_id.isNullOrBlank()) ZoneId.systemDefault() else ZoneId.of(location.tz_id)
127+
val zoneId = if (location.tz_id.isNullOrBlank()) null else ZoneId.of(location.tz_id)
134128
return City(
135129
0, locationId, location.name, location.region, location.country,
136130
location.lat, location.lon, zoneId

0 commit comments

Comments
 (0)