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