1 package com.android.systemui.plugins
2 
3 import android.os.Bundle
4 import android.util.Log
5 import android.view.View
6 import androidx.annotation.VisibleForTesting
7 
8 typealias WeatherTouchAction = (View) -> Unit
9 
10 class WeatherData
11 constructor(
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 
27         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 
58         private fun readIntFromBundle(extras: Bundle, key: String): Int? =
59             try {
60                 extras.getString(key)?.toInt()
61             } catch (e: Exception) {
62                 null
63             }
64     }
65 
66     // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
67     enum class WeatherStateIcon(val id: Int) {
68         UNKNOWN_ICON(0),
69 
70         // Clear, day & night.
71         SUNNY(1),
72         CLEAR_NIGHT(2),
73 
74         // Mostly clear, day & night.
75         MOSTLY_SUNNY(3),
76         MOSTLY_CLEAR_NIGHT(4),
77 
78         // Partly cloudy, day & night.
79         PARTLY_CLOUDY(5),
80         PARTLY_CLOUDY_NIGHT(6),
81 
82         // Mostly cloudy, day & night.
83         MOSTLY_CLOUDY_DAY(7),
84         MOSTLY_CLOUDY_NIGHT(8),
85         CLOUDY(9),
86         HAZE_FOG_DUST_SMOKE(10),
87         DRIZZLE(11),
88         HEAVY_RAIN(12),
89         SHOWERS_RAIN(13),
90 
91         // Scattered showers, day & night.
92         SCATTERED_SHOWERS_DAY(14),
93         SCATTERED_SHOWERS_NIGHT(15),
94 
95         // Isolated scattered thunderstorms, day & night.
96         ISOLATED_SCATTERED_TSTORMS_DAY(16),
97         ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
98         STRONG_TSTORMS(18),
99         BLIZZARD(19),
100         BLOWING_SNOW(20),
101         FLURRIES(21),
102         HEAVY_SNOW(22),
103 
104         // Scattered snow showers, day & night.
105         SCATTERED_SNOW_SHOWERS_DAY(23),
106         SCATTERED_SNOW_SHOWERS_NIGHT(24),
107         SNOW_SHOWERS_SNOW(25),
108         MIXED_RAIN_HAIL_RAIN_SLEET(26),
109         SLEET_HAIL(27),
110         TORNADO(28),
111         TROPICAL_STORM_HURRICANE(29),
112         WINDY_BREEZY(30),
113         WINTRY_MIX_RAIN_SNOW(31);
114 
115         companion object {
116             fun fromInt(value: Int) = values().firstOrNull { it.id == value }
117         }
118     }
119 
120     override fun toString(): String {
121         val unit = if (useCelsius) "C" else "F"
122         return "$state (\"$description\") $temperature°$unit"
123     }
124 }
125