xref: /aosp_15_r20/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 package com.android.systemui.plugins.clocks
2 
3 import android.os.Bundle
4 import android.util.Log
5 import android.view.View
6 import androidx.annotation.VisibleForTesting
7 import androidx.core.text.util.LocalePreferences
8 
9 typealias WeatherTouchAction = (View) -> Unit
10 
11 data class WeatherData(
12     val description: String,
13     val state: WeatherStateIcon,
14     val useCelsius: Boolean,
15     val temperature: Int,
16     val touchAction: WeatherTouchAction? = null,
17 ) {
18     companion object {
19         const val DEBUG = true
20         private const val TAG = "WeatherData"
21         @VisibleForTesting const val DESCRIPTION_KEY = "description"
22         @VisibleForTesting const val STATE_KEY = "state"
23         @VisibleForTesting const val USE_CELSIUS_KEY = "use_celsius"
24         @VisibleForTesting const val TEMPERATURE_KEY = "temperature"
25         private const val INVALID_WEATHER_ICON_STATE = -1
26 
fromBundlenull27         fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? {
28             val description = extras.getString(DESCRIPTION_KEY)
29             val state =
30                 WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
31             val temperature = readIntFromBundle(extras, TEMPERATURE_KEY)
32             if (
33                 description == null ||
34                     state == null ||
35                     !extras.containsKey(USE_CELSIUS_KEY) ||
36                     temperature == null
37             ) {
38                 if (DEBUG) {
39                     Log.w(TAG, "Weather data did not parse from $extras")
40                 }
41                 return null
42             } else {
43                 val result =
44                     WeatherData(
45                         description = description,
46                         state = state,
47                         useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
48                         temperature = temperature,
49                         touchAction = touchAction
50                     )
51                 if (DEBUG) {
52                     Log.i(TAG, "Weather data parsed $result from $extras")
53                 }
54                 return result
55             }
56         }
57 
readIntFromBundlenull58         private fun readIntFromBundle(extras: Bundle, key: String): Int? {
59             try {
60                 return extras.getString(key)?.toInt()
61             } catch (e: Exception) {
62                 return null
63             }
64         }
65 
getPlaceholderWeatherDatanull66         fun getPlaceholderWeatherData(): WeatherData {
67             return getPlaceholderWeatherData(
68                 LocalePreferences.getTemperatureUnit() == LocalePreferences.TemperatureUnit.CELSIUS
69             )
70         }
71 
72         private const val DESCRIPTION_PLACEHODLER = ""
73         private const val TEMPERATURE_FAHRENHEIT_PLACEHOLDER = 58
74         private const val TEMPERATURE_CELSIUS_PLACEHOLDER = 21
75         private val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
76 
getPlaceholderWeatherDatanull77         fun getPlaceholderWeatherData(useCelsius: Boolean): WeatherData {
78             return WeatherData(
79                 description = DESCRIPTION_PLACEHODLER,
80                 state = WEATHERICON_PLACEHOLDER,
81                 temperature =
82                     if (useCelsius) TEMPERATURE_CELSIUS_PLACEHOLDER
83                     else TEMPERATURE_FAHRENHEIT_PLACEHOLDER,
84                 useCelsius = useCelsius,
85             )
86         }
87     }
88 
89     // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
90     enum class WeatherStateIcon(val id: Int) {
91         UNKNOWN_ICON(0),
92 
93         // Clear, day & night.
94         SUNNY(1),
95         CLEAR_NIGHT(2),
96 
97         // Mostly clear, day & night.
98         MOSTLY_SUNNY(3),
99         MOSTLY_CLEAR_NIGHT(4),
100 
101         // Partly cloudy, day & night.
102         PARTLY_CLOUDY(5),
103         PARTLY_CLOUDY_NIGHT(6),
104 
105         // Mostly cloudy, day & night.
106         MOSTLY_CLOUDY_DAY(7),
107         MOSTLY_CLOUDY_NIGHT(8),
108         CLOUDY(9),
109         HAZE_FOG_DUST_SMOKE(10),
110         DRIZZLE(11),
111         HEAVY_RAIN(12),
112         SHOWERS_RAIN(13),
113 
114         // Scattered showers, day & night.
115         SCATTERED_SHOWERS_DAY(14),
116         SCATTERED_SHOWERS_NIGHT(15),
117 
118         // Isolated scattered thunderstorms, day & night.
119         ISOLATED_SCATTERED_TSTORMS_DAY(16),
120         ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
121         STRONG_TSTORMS(18),
122         BLIZZARD(19),
123         BLOWING_SNOW(20),
124         FLURRIES(21),
125         HEAVY_SNOW(22),
126 
127         // Scattered snow showers, day & night.
128         SCATTERED_SNOW_SHOWERS_DAY(23),
129         SCATTERED_SNOW_SHOWERS_NIGHT(24),
130         SNOW_SHOWERS_SNOW(25),
131         MIXED_RAIN_HAIL_RAIN_SLEET(26),
132         SLEET_HAIL(27),
133         TORNADO(28),
134         TROPICAL_STORM_HURRICANE(29),
135         WINDY_BREEZY(30),
136         WINTRY_MIX_RAIN_SNOW(31);
137 
138         companion object {
<lambda>null139             fun fromInt(value: Int) = values().firstOrNull { it.id == value }
140         }
141     }
142 
toStringnull143     override fun toString(): String {
144         val unit = if (useCelsius) "C" else "F"
145         return "$state (\"$description\") $temperature°$unit"
146     }
147 }
148