Blog Infos
Author
Published
Topics
,
Published

Weather App Preview

 

Flow of the article:
  1. What is Jetpack Compose for Wear OS?
  2. Fundamentals of Wear OS.
  3. Building Weather App for wearables
What is Jetpack Compose for Wear OS?

Now, To Build applications for Wearable devices. we can now use jetpack compose. We can use Jetpack Compose for building applications for Wearable devices by providing functionality to support wearable-specific devices, sizes, shapes, and navigation gestures.

Fundamental of Wear OS.

Building for Wearable apps is the same as for mobile apps. Just we have to change a few dependencies. You can use mobile dependencies but they are not optimized for wear os.

These are the differences between wearable and mobile dependencies

Now, I will explain the composables which we are going to use in our app.

Text: this is similar to the mobile dependency version.

Button: this is similar to the mobile dependency version.

ScalingLazyColumn: this is the wear os version of LazColumn is little different as for rounded watches top and bottom have less space to provide a better experience we use this.

Building Weather App for wearables.

Now, let us build the app. Here is the github link

So our task is to get the watch location and using the location we hit an open weather API to get the weather of the current location and display it on the app.

Step 1: Create a project and choose wear os and give the project any name you want.

Step 2: You need to add a few dependencies in build.gradle(:App)

//request permission
implementation "com.google.accompanist:accompanist-permissions:0.25.0"
//location
implementation 'com.google.android.gms:play-services-location:20.0.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
view raw build.gradle hosted with ❤ by GitHub

Step 3: Add permissions in Mainfest.xml

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
view raw Mainfest.xml hosted with ❤ by GitHub

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Creating your first Wear OS app using compose

This will be an introductory session into the world of wearable apps. In this session we’ll discuss how to create a latest up to date wearable app by using “Compose for Wear OS” and Horologist…
Watch Video

Creating your first Wear OS app using compose

Mikhail Kulaha
Compose engineer
Google

Creating your first Wear OS app using compose

Mikhail Kulaha
Compose engineer
Google

Creating your first Wear OS app using compose

Mikhail Kulaha
Compose engineer
Google

Jobs

Step 4: Now our task would be to get location permissions.

a. we first request location permission.

here we are requesting multiple permissions ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. To check all permissions are granted

locationPermissionsState.allPermissionsGranted

if permissions are granted we will fetch the location. Otherwise, we will show the Location Icon to ask for permission.

val locationPermissionsState = rememberMultiplePermissionsState(
listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
)
if (locationPermissionsState.allPermissionsGranted){
if (!locationUtil.dataLoaded.value)
item { TextWidget(contentModifier,"Thanks! I can access your exact location :D") }
else{
item { CardWidget(
modifier = contentModifier,
title = locationUtil.data.value.name ,
weatherDescription = locationUtil.data.value.weatherDescription,
time = locationUtil.data.value.time , temperature = 12.0)
}
}
}
else{
val allPermissionsRevoked =
locationPermissionsState.permissions.size ==
locationPermissionsState.revokedPermissions.size
val textToShow = if (!allPermissionsRevoked) {
"Yay! Thanks for letting me access your approximate location. " +
"But you know what would be great? If you allow me to know where you " +
"exactly are. Thank you!"
} else if (locationPermissionsState.shouldShowRationale) {
"Getting your exact location is important for this app. " +
"Please grant us fine location. Thank you :D"
} else {
"This feature requires location permission"
}
item { TextExample(contentModifier,textToShow) }
item { ButtonWidget(contentModifier, iconModifier){
locationPermissionsState.launchMultiplePermissionRequest()
} }
}
view raw WearApp.kt hosted with ❤ by GitHub

location button

 

b. we will create a WeatherApi interface for making network requests to get weather details.

interface WeatherApi {
companion object {
const val BASE_URL = "https://api.openweathermap.org/"
val apiInstance = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)).build())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(WeatherApi::class.java)
}
@GET("/data/2.5/weather")
suspend fun getWeatherDetails(@Query("lat") lat: Double,@Query("lon") long: Double,@Query("appid") appid: String): WeatherDTO
}
view raw WeatherApi.kt hosted with ❤ by GitHub

c. we now create an instance of FusedLocationProviderClient. it will directly take the location of the mobile if the watch doesn’t support GPS.

fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
fun createLocationRequest(context: Context,fusedLocationClient: FusedLocationProviderClient) {
val locationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 1000
priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
}
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationClient.requestLocationUpdates(locationRequest,object : LocationCallback() {
override fun onLocationResult(p0: LocationResult) {
for (location in p0.locations){
CoroutineScope(Dispatchers.IO).launch {
val weatherDTO = WeatherApi
.apiInstance
.getWeatherDetails(location.latitude,
location.longitude,
context.resources.getString(com.example.wear2.R.string.open_weather_api_key))
dataLoaded.value = true;
data.value = AppCardData(name = weatherDTO.name,
time = "${(weatherDTO.main.temp - 273).roundToInt()}°C",
weatherDescription = weatherDTO.weather[0].description,
temp = (weatherDTO.main.temp - 273).roundToInt().toDouble()
)
}
}
}
}, Looper.getMainLooper())
}
view raw location.kt hosted with ❤ by GitHub

here, we will set locationRequest defining the priority and interval. then we will call for location updates. and once we get the location we will hit the GET request.

Note: create account on openweather and add your api in strings.xml

Step 5: Now, we will create our UI.

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun WearApp(locationUtil: LocationUtil) {
val listState = rememberScalingLazyListState()
Wear2Theme {
val locationPermissionsState = rememberMultiplePermissionsState(
listOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
)
)
val contentModifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
val iconModifier = Modifier
.size(24.dp)
.wrapContentSize(align = Alignment.Center)
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = 32.dp,
start = 8.dp,
end = 8.dp,
bottom = 32.dp
),
verticalArrangement = Arrangement.Bottom,
state = listState,
autoCentering = true
) {
if (locationPermissionsState.allPermissionsGranted){
if (!locationUtil.dataLoaded.value)
item { TextWidget(contentModifier,"Thanks! I can access your exact location :D") }
else{
item { CardWidget(
modifier = contentModifier,
title = locationUtil.data.value.name ,
weatherDescription = locationUtil.data.value.weatherDescription,
time = locationUtil.data.value.time , temperature = 12.0)
}
}
}
else{
val allPermissionsRevoked =
locationPermissionsState.permissions.size ==
locationPermissionsState.revokedPermissions.size
val textToShow = if (!allPermissionsRevoked) {
"Yay! Thanks for letting me access your approximate location. " +
"But you know what would be great? If you allow me to know where you " +
"exactly are. Thank you!"
} else if (locationPermissionsState.shouldShowRationale) {
"Getting your exact location is important for this app. " +
"Please grant us fine location. Thank you :D"
} else {
"This feature requires location permission"
}
item { TextWidget(contentModifier,textToShow) }
item { ButtonWidget(contentModifier, iconModifier){
locationPermissionsState.launchMultiplePermissionRequest()
} }
}
}
}
}
view raw WearApp.kt hosted with ❤ by GitHub

This is our main UI where we will define ScalingLazyColumn and add items. I have created mutableState in LocationUtil.kt which tells whether we got location updates or not in UI and according to that we will show the card or the text and as we get data we will fill the CardWidget with data.

 

This is the UI

 

Now, you have created a weather application for wearables

Thank You for reading

This article was originally published on proandroiddev.com on October 08, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu