From 24f7ff69ed5c63a10fd8277c514a377a68caec71 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:01:12 +0000 Subject: [PATCH] Create monochromatic Android weather app for MV and PA This CL creates a new Android application that displays hourly weather forecasts for Mountain View and Palo Alto, CA. Features: - Fetches weather data from Open-Meteo API. - Displays hourly temperature for both locations side-by-side. - Uses a monochromatic UI (black background, white/gray text). - Includes Retrofit for networking and Gson for JSON parsing. - Uses RecyclerView for efficient list display. - Handles timezones correctly (America/Los_Angeles). The project structure includes: - Standard Android project layout (app module, src/main/java, src/main/res). - Gradle build configuration. - AndroidManifest with INTERNET permission. Co-authored-by: hassler-google <130589279+hassler-google@users.noreply.github.com> --- android_weather_app/.gitignore | 4 ++ android_weather_app/app/build.gradle | 36 ++++++++++ .../app/src/main/AndroidManifest.xml | 15 ++++ .../java/com/example/weatherapp/Hourly.java | 13 ++++ .../com/example/weatherapp/MainActivity.java | 70 +++++++++++++++++++ .../example/weatherapp/WeatherAdapter.java | 69 ++++++++++++++++++ .../example/weatherapp/WeatherResponse.java | 11 +++ .../example/weatherapp/WeatherService.java | 16 +++++ .../app/src/main/res/layout/activity_main.xml | 65 +++++++++++++++++ .../app/src/main/res/layout/item_weather.xml | 38 ++++++++++ .../app/src/main/res/values/colors.xml | 12 ++++ .../app/src/main/res/values/styles.xml | 10 +++ android_weather_app/build.gradle | 20 ++++++ android_weather_app/gradle.properties | 1 + android_weather_app/settings.gradle | 1 + 15 files changed, 381 insertions(+) create mode 100644 android_weather_app/.gitignore create mode 100644 android_weather_app/app/build.gradle create mode 100644 android_weather_app/app/src/main/AndroidManifest.xml create mode 100644 android_weather_app/app/src/main/java/com/example/weatherapp/Hourly.java create mode 100644 android_weather_app/app/src/main/java/com/example/weatherapp/MainActivity.java create mode 100644 android_weather_app/app/src/main/java/com/example/weatherapp/WeatherAdapter.java create mode 100644 android_weather_app/app/src/main/java/com/example/weatherapp/WeatherResponse.java create mode 100644 android_weather_app/app/src/main/java/com/example/weatherapp/WeatherService.java create mode 100644 android_weather_app/app/src/main/res/layout/activity_main.xml create mode 100644 android_weather_app/app/src/main/res/layout/item_weather.xml create mode 100644 android_weather_app/app/src/main/res/values/colors.xml create mode 100644 android_weather_app/app/src/main/res/values/styles.xml create mode 100644 android_weather_app/build.gradle create mode 100644 android_weather_app/gradle.properties create mode 100644 android_weather_app/settings.gradle diff --git a/android_weather_app/.gitignore b/android_weather_app/.gitignore new file mode 100644 index 0000000..a7e04a2 --- /dev/null +++ b/android_weather_app/.gitignore @@ -0,0 +1,4 @@ +.gradle/ +build/ +app/build/ +local.properties diff --git a/android_weather_app/app/build.gradle b/android_weather_app/app/build.gradle new file mode 100644 index 0000000..33740ec --- /dev/null +++ b/android_weather_app/app/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.example.weatherapp' + compileSdk 34 + + defaultConfig { + applicationId "com.example.weatherapp" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.recyclerview:recyclerview:1.3.1' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' +} diff --git a/android_weather_app/app/src/main/AndroidManifest.xml b/android_weather_app/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5ac0729 --- /dev/null +++ b/android_weather_app/app/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/android_weather_app/app/src/main/java/com/example/weatherapp/Hourly.java b/android_weather_app/app/src/main/java/com/example/weatherapp/Hourly.java new file mode 100644 index 0000000..5039b1a --- /dev/null +++ b/android_weather_app/app/src/main/java/com/example/weatherapp/Hourly.java @@ -0,0 +1,13 @@ +package com.example.weatherapp; + +import java.util.List; +import com.google.gson.annotations.SerializedName; + +public class Hourly { + private List time; + @SerializedName("temperature_2m") + private List temperature2m; + + public List getTime() { return time; } + public List getTemperature2m() { return temperature2m; } +} diff --git a/android_weather_app/app/src/main/java/com/example/weatherapp/MainActivity.java b/android_weather_app/app/src/main/java/com/example/weatherapp/MainActivity.java new file mode 100644 index 0000000..c5f25c5 --- /dev/null +++ b/android_weather_app/app/src/main/java/com/example/weatherapp/MainActivity.java @@ -0,0 +1,70 @@ +package com.example.weatherapp; + +import android.os.Bundle; +import android.util.Log; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class MainActivity extends AppCompatActivity { + + private RecyclerView recyclerView; + private WeatherAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + recyclerView = findViewById(R.id.recycler_view); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + fetchWeather(); + } + + private void fetchWeather() { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl("https://api.open-meteo.com/") + .addConverterFactory(GsonConverterFactory.create()) + .build(); + + WeatherService service = retrofit.create(WeatherService.class); + + Call> call = service.getForecast( + "37.3861,37.4419", + "-122.0839,-122.1430", + "temperature_2m", + "America/Los_Angeles" + ); + + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (response.isSuccessful() && response.body() != null && response.body().size() >= 2) { + WeatherResponse mvData = response.body().get(0); + WeatherResponse paData = response.body().get(1); + + List times = mvData.getHourly().getTime(); + List tempsMV = mvData.getHourly().getTemperature2m(); + List tempsPA = paData.getHourly().getTemperature2m(); + + adapter = new WeatherAdapter(times, tempsMV, tempsPA); + recyclerView.setAdapter(adapter); + } else { + Log.e("WeatherApp", "Response unsuccessful or empty"); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + Log.e("WeatherApp", "API Call failed", t); + } + }); + } +} diff --git a/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherAdapter.java b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherAdapter.java new file mode 100644 index 0000000..65acbe2 --- /dev/null +++ b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherAdapter.java @@ -0,0 +1,69 @@ +package com.example.weatherapp; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import java.util.List; + +public class WeatherAdapter extends RecyclerView.Adapter { + + private List times; + private List tempsMV; + private List tempsPA; + + public WeatherAdapter(List times, List tempsMV, List tempsPA) { + this.times = times; + this.tempsMV = tempsMV; + this.tempsPA = tempsPA; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_weather, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + String time = times.get(position); + if (time.contains("T")) { + time = time.split("T")[1]; + } + + holder.tvTime.setText(time); + + if (position < tempsMV.size()) { + holder.tvMvTemp.setText(String.format("%.1f°C", tempsMV.get(position))); + } else { + holder.tvMvTemp.setText(""); + } + + if (position < tempsPA.size()) { + holder.tvPaTemp.setText(String.format("%.1f°C", tempsPA.get(position))); + } else { + holder.tvPaTemp.setText(""); + } + } + + @Override + public int getItemCount() { + return times != null ? times.size() : 0; + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView tvTime; + TextView tvMvTemp; + TextView tvPaTemp; + + ViewHolder(View itemView) { + super(itemView); + tvTime = itemView.findViewById(R.id.tv_time); + tvMvTemp = itemView.findViewById(R.id.tv_mv_temp); + tvPaTemp = itemView.findViewById(R.id.tv_pa_temp); + } + } +} diff --git a/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherResponse.java b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherResponse.java new file mode 100644 index 0000000..202e616 --- /dev/null +++ b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherResponse.java @@ -0,0 +1,11 @@ +package com.example.weatherapp; + +public class WeatherResponse { + private double latitude; + private double longitude; + private Hourly hourly; + + public double getLatitude() { return latitude; } + public double getLongitude() { return longitude; } + public Hourly getHourly() { return hourly; } +} diff --git a/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherService.java b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherService.java new file mode 100644 index 0000000..22257ac --- /dev/null +++ b/android_weather_app/app/src/main/java/com/example/weatherapp/WeatherService.java @@ -0,0 +1,16 @@ +package com.example.weatherapp; + +import java.util.List; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface WeatherService { + @GET("v1/forecast") + Call> getForecast( + @Query("latitude") String latitude, + @Query("longitude") String longitude, + @Query("hourly") String hourly, + @Query("timezone") String timezone + ); +} diff --git a/android_weather_app/app/src/main/res/layout/activity_main.xml b/android_weather_app/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..8a0d028 --- /dev/null +++ b/android_weather_app/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + diff --git a/android_weather_app/app/src/main/res/layout/item_weather.xml b/android_weather_app/app/src/main/res/layout/item_weather.xml new file mode 100644 index 0000000..5b952c9 --- /dev/null +++ b/android_weather_app/app/src/main/res/layout/item_weather.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/android_weather_app/app/src/main/res/values/colors.xml b/android_weather_app/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..fe9bad5 --- /dev/null +++ b/android_weather_app/app/src/main/res/values/colors.xml @@ -0,0 +1,12 @@ + + + #FF000000 + #FFFFFFFF + #FF333333 + #FFCCCCCC + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + diff --git a/android_weather_app/app/src/main/res/values/styles.xml b/android_weather_app/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..4e61d29 --- /dev/null +++ b/android_weather_app/app/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android_weather_app/build.gradle b/android_weather_app/build.gradle new file mode 100644 index 0000000..252492e --- /dev/null +++ b/android_weather_app/build.gradle @@ -0,0 +1,20 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android_weather_app/gradle.properties b/android_weather_app/gradle.properties new file mode 100644 index 0000000..5bac8ac --- /dev/null +++ b/android_weather_app/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX=true diff --git a/android_weather_app/settings.gradle b/android_weather_app/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android_weather_app/settings.gradle @@ -0,0 +1 @@ +include ':app'