Skip to content

Commit 49bc9ce

Browse files
committed
api call for London weather
1 parent a73618b commit 49bc9ce

File tree

24 files changed

+413
-11
lines changed

24 files changed

+413
-11
lines changed

.idea/gradle.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ReadMe.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
## Android Weather App
2+
3+
### Features
4+
5+
### Learning Outcome
6+
7+
- **Storing Secret Information:** For api calls we need api key. But this key should not be pushed to public repositories. Therefore, the right way to store and pass the apikey or any sensitive data during build time is adding it in `local.properties` in project root.
8+
```properties
9+
# local.properties
10+
WEATHER_API_KEY=my-secret-key
11+
```
12+
Now to get this value during build time we add this in our module level `BuildConfig.java`. To do this open module level `build.gradle` add add these lines
13+
```groovy
14+
android {
15+
16+
// other configurations here
17+
18+
def localProperties = new Property()
19+
def localPropertiesFile = rootProject.file('local.properties')
20+
if (localPropertiesFile.exists()) {
21+
localPropertiesFile.withReader('UTF-8') {
22+
localProperties.load(it)
23+
}
24+
}
25+
26+
buildFeature {
27+
buildConfig = true // must add otherwise show warning
28+
}
29+
30+
buildConfigField( // adds new field to the module level BuildConfig.java
31+
"String", // field type
32+
"WEATHER_KEY_API", // field name
33+
"\"${localProperties.getPerperty('WEATHER_API_KEY')}\"" // field value, for string must add double quote
34+
)
35+
}
36+
```
37+
Next build the project to create the BuildConfig file. Since local.properties and BuildConfig.java both are ignored during git push so the sensitive data are safe.
38+
**Note** make sure in `.gitignore` at project root directory and local.properties is added.
39+
40+
- **API call using Retrofit2:** First of all I need to add the retrofit2 as well as one of the converters provided by Retrofit2 library. This converter is used serializing and deserializing the request and response.
41+
Like for this project gson converter is used so all the request body will be serialized to json and response body will be deserialized from json. During the retrofit build use `addConverterFactory(...)` method to add required converter.
42+
- **Request URL:** Retrofit base url must end with `/` otherwise error occurs. Like this https://api.weatherapi.com/v1/. Also remember that if in service interface http method annotation, like GET POST etc., if `/` is prepended then
43+
path will be considered relative the domain part. So don't prepend `/` in service method url paths. For example: if `@GET("/current.json")` then requests to https://api.weatherapi.com/current.json not to https://api.weatherapi.com/v1/current.json .
44+
- **OkHttpClient Interceptor:** In cases when I need to something for each request, then we can use OkHttpClient `Interceptor` interface to manipulate each request before sending. For example: this is project we need to append a `key` query which
45+
contains the api key value. I added an Interceptor that appends `key` query parameter to each request.
46+
47+
### Change Log
48+
49+
- **v1.0**
50+
simple api call and display the current weather report for city **London** on success or log error body if request fails.
51+
If any other exception occurs then a `Toast` message is displayed.

app/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ android {
99

1010
defaultConfig {
1111
applicationId "rahulstech.android.weatherapp"
12-
minSdk 22
12+
minSdk 26
1313
targetSdk 34
1414
versionCode 1
1515
versionName "1.0"
@@ -48,6 +48,8 @@ android {
4848

4949
dependencies {
5050

51+
implementation project(':backend:weather-api')
52+
5153
implementation libs.androidx.core.ktx
5254
implementation libs.androidx.appcompat
5355
implementation libs.material
Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,67 @@
11
package rahulstech.android.weatherapp.activity
22

33
import android.os.Bundle
4-
import androidx.activity.enableEdgeToEdge
4+
import android.util.Log
5+
import android.widget.TextView
6+
import android.widget.Toast
57
import androidx.appcompat.app.AppCompatActivity
6-
import androidx.core.view.ViewCompat
7-
import androidx.core.view.WindowInsetsCompat
8+
import com.weather.api.WeatherClient
9+
import com.weather.api.model.CurrentWeatherReport
810
import rahulstech.android.weatherapp.BuildConfig
911
import rahulstech.android.weatherapp.R
12+
import retrofit2.Call
13+
import retrofit2.Callback
14+
import retrofit2.Response
1015

1116
class HomeActivity : AppCompatActivity() {
17+
18+
private val TAG = HomeActivity::class.java.simpleName
19+
20+
private var labelCity: TextView? = null
21+
22+
private var labelTemperature: TextView? = null
23+
1224
override fun onCreate(savedInstanceState: Bundle?) {
1325
super.onCreate(savedInstanceState)
14-
enableEdgeToEdge()
1526
setContentView(R.layout.activity_home)
16-
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
17-
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
18-
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
19-
insets
20-
}
27+
28+
labelCity = findViewById(R.id.label_city)
29+
labelTemperature = findViewById(R.id.label_temperature)
30+
31+
32+
WeatherClient(BuildConfig.WEATHER_API_KEY)
33+
.getWeatherService()?.getCurrentWeatherAsync("London")
34+
?.enqueue(object : Callback<CurrentWeatherReport>{
35+
override fun onResponse(
36+
call: Call<CurrentWeatherReport>,
37+
res: Response<CurrentWeatherReport>
38+
) {
39+
if (res.isSuccessful) {
40+
onCurrentWeatherReportFetched(res.body())
41+
}
42+
else {
43+
onRequestFail(res.code(), res.errorBody()?.string())
44+
}
45+
}
46+
47+
override fun onFailure(call: Call<CurrentWeatherReport>, err: Throwable) {
48+
Log.e(TAG, "current weather fetch error ", err)
49+
Toast.makeText(this@HomeActivity, "can not fetch current weather", Toast.LENGTH_SHORT).show()
50+
}
51+
})
52+
}
53+
54+
private fun onCurrentWeatherReportFetched(report: CurrentWeatherReport?) {
55+
val city = report?.location?.name
56+
val tempC = report?.current?.temp_c
57+
58+
Log.i(TAG,"city=$city tempC=$tempC")
59+
60+
labelCity?.text = city
61+
labelTemperature?.text = tempC.toString()
62+
}
63+
64+
private fun onRequestFail(resCode: Int, body: String?) {
65+
Log.e(TAG, "request failed with res-code=$resCode err-body: $body")
2166
}
2267
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,20 @@
77
android:layout_height="match_parent"
88
tools:context=".activity.HomeActivity">
99

10+
<TextView
11+
android:id="@+id/label_city"
12+
android:layout_width="wrap_content"
13+
android:layout_height="wrap_content"
14+
app:layout_constraintStart_toStartOf="parent"
15+
app:layout_constraintEnd_toEndOf="parent"
16+
app:layout_constraintTop_toTopOf="parent" />
17+
18+
<TextView
19+
android:id="@+id/label_temperature"
20+
android:layout_width="wrap_content"
21+
android:layout_height="wrap_content"
22+
app:layout_constraintTop_toBottomOf="@id/label_city"
23+
app:layout_constraintStart_toStartOf="parent"
24+
app:layout_constraintEnd_toEndOf="parent"/>
25+
1026
</androidx.constraintlayout.widget.ConstraintLayout>

backend/weather-api/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

backend/weather-api/build.gradle

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
plugins {
2+
alias(libs.plugins.android.library)
3+
alias(libs.plugins.kotlin.android)
4+
}
5+
6+
android {
7+
namespace 'com.weather.api'
8+
compileSdk 34
9+
10+
defaultConfig {
11+
minSdk 26
12+
13+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14+
consumerProguardFiles "consumer-rules.pro"
15+
}
16+
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21+
}
22+
}
23+
compileOptions {
24+
sourceCompatibility JavaVersion.VERSION_11
25+
targetCompatibility JavaVersion.VERSION_11
26+
}
27+
kotlinOptions {
28+
jvmTarget = '11'
29+
}
30+
}
31+
32+
dependencies {
33+
34+
api libs.retrofit
35+
api libs.retrofir.converter.gson
36+
37+
implementation libs.androidx.core.ktx
38+
implementation libs.androidx.appcompat
39+
testImplementation libs.junit
40+
androidTestImplementation libs.androidx.junit
41+
}

backend/weather-api/consumer-rules.pro

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

0 commit comments

Comments
 (0)