Merge "Read weather data from smartspace extras" into tm-qpr-dev am: 41b93c6eea

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21274075

Change-Id: I77b38628ec43bc7cb10fb6df29e976cb87757958
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Peter Kalauskas
2023-02-22 01:34:58 +00:00
committed by Automerger Merge Worker
8 changed files with 361 additions and 109 deletions

View File

@@ -113,7 +113,7 @@ interface ClockEvents {
fun onColorPaletteChanged(resources: Resources) {} fun onColorPaletteChanged(resources: Resources) {}
/** Call whenever the weather data should update */ /** Call whenever the weather data should update */
fun onWeatherDataChanged(data: Weather) {} fun onWeatherDataChanged(data: WeatherData) {}
} }
/** Methods which trigger various clock animations */ /** Methods which trigger various clock animations */

View File

@@ -1,85 +0,0 @@
package com.android.systemui.plugins
import android.os.Bundle
class Weather(val conditions: WeatherStateIcon, val temperature: Int, val isCelsius: Boolean) {
companion object {
private const val TAG = "Weather"
private const val WEATHER_STATE_ICON_KEY = "weather_state_icon_extra_key"
private const val TEMPERATURE_VALUE_KEY = "temperature_value_extra_key"
private const val TEMPERATURE_UNIT_KEY = "temperature_unit_extra_key"
private const val INVALID_TEMPERATURE = Int.MIN_VALUE
fun fromBundle(extras: Bundle): Weather? {
val icon =
WeatherStateIcon.fromInt(
extras.getInt(WEATHER_STATE_ICON_KEY, WeatherStateIcon.UNKNOWN_ICON.id)
)
if (icon == null || icon == WeatherStateIcon.UNKNOWN_ICON) {
return null
}
val temperature = extras.getInt(TEMPERATURE_VALUE_KEY, INVALID_TEMPERATURE)
if (temperature == INVALID_TEMPERATURE) {
return null
}
return Weather(icon, temperature, extras.getBoolean(TEMPERATURE_UNIT_KEY))
}
}
enum class WeatherStateIcon(val id: Int) {
UNKNOWN_ICON(0),
// Clear, day & night.
SUNNY(1),
CLEAR_NIGHT(2),
// Mostly clear, day & night.
MOSTLY_SUNNY(3),
MOSTLY_CLEAR_NIGHT(4),
// Partly cloudy, day & night.
PARTLY_CLOUDY(5),
PARTLY_CLOUDY_NIGHT(6),
// Mostly cloudy, day & night.
MOSTLY_CLOUDY_DAY(7),
MOSTLY_CLOUDY_NIGHT(8),
CLOUDY(9),
HAZE_FOG_DUST_SMOKE(10),
DRIZZLE(11),
HEAVY_RAIN(12),
SHOWERS_RAIN(13),
// Scattered showers, day & night.
SCATTERED_SHOWERS_DAY(14),
SCATTERED_SHOWERS_NIGHT(15),
// Isolated scattered thunderstorms, day & night.
ISOLATED_SCATTERED_TSTORMS_DAY(16),
ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
STRONG_TSTORMS(18),
BLIZZARD(19),
BLOWING_SNOW(20),
FLURRIES(21),
HEAVY_SNOW(22),
// Scattered snow showers, day & night.
SCATTERED_SNOW_SHOWERS_DAY(23),
SCATTERED_SNOW_SHOWERS_NIGHT(24),
SNOW_SHOWERS_SNOW(25),
MIXED_RAIN_HAIL_RAIN_SLEET(26),
SLEET_HAIL(27),
TORNADO(28),
TROPICAL_STORM_HURRICANE(29),
WINDY_BREEZY(30),
WINTRY_MIX_RAIN_SNOW(31);
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.id == value }
}
}
override fun toString(): String {
return "$conditions $temperature${if (isCelsius) "C" else "F"}"
}
}

View File

@@ -0,0 +1,107 @@
package com.android.systemui.plugins
import android.os.Bundle
import androidx.annotation.VisibleForTesting
class WeatherData
private constructor(
val description: String,
val state: WeatherStateIcon,
val useCelsius: Boolean,
val temperature: Int,
) {
companion object {
private const val TAG = "WeatherData"
@VisibleForTesting const val DESCRIPTION_KEY = "description"
@VisibleForTesting const val STATE_KEY = "state"
@VisibleForTesting const val USE_CELSIUS_KEY = "use_celsius"
@VisibleForTesting const val TEMPERATURE_KEY = "temperature"
private const val INVALID_WEATHER_ICON_STATE = -1
fun fromBundle(extras: Bundle): WeatherData? {
val description = extras.getString(DESCRIPTION_KEY)
val state =
WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
val temperature = readIntFromBundle(extras, TEMPERATURE_KEY)
return if (
description == null ||
state == null ||
!extras.containsKey(USE_CELSIUS_KEY) ||
temperature == null
)
null
else
WeatherData(
description = description,
state = state,
useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
temperature = temperature
)
}
private fun readIntFromBundle(extras: Bundle, key: String): Int? =
try {
extras.getString(key).toInt()
} catch (e: Exception) {
null
}
}
enum class WeatherStateIcon(val id: Int) {
UNKNOWN_ICON(0),
// Clear, day & night.
SUNNY(1),
CLEAR_NIGHT(2),
// Mostly clear, day & night.
MOSTLY_SUNNY(3),
MOSTLY_CLEAR_NIGHT(4),
// Partly cloudy, day & night.
PARTLY_CLOUDY(5),
PARTLY_CLOUDY_NIGHT(6),
// Mostly cloudy, day & night.
MOSTLY_CLOUDY_DAY(7),
MOSTLY_CLOUDY_NIGHT(8),
CLOUDY(9),
HAZE_FOG_DUST_SMOKE(10),
DRIZZLE(11),
HEAVY_RAIN(12),
SHOWERS_RAIN(13),
// Scattered showers, day & night.
SCATTERED_SHOWERS_DAY(14),
SCATTERED_SHOWERS_NIGHT(15),
// Isolated scattered thunderstorms, day & night.
ISOLATED_SCATTERED_TSTORMS_DAY(16),
ISOLATED_SCATTERED_TSTORMS_NIGHT(17),
STRONG_TSTORMS(18),
BLIZZARD(19),
BLOWING_SNOW(20),
FLURRIES(21),
HEAVY_SNOW(22),
// Scattered snow showers, day & night.
SCATTERED_SNOW_SHOWERS_DAY(23),
SCATTERED_SNOW_SHOWERS_NIGHT(24),
SNOW_SHOWERS_SNOW(25),
MIXED_RAIN_HAIL_RAIN_SLEET(26),
SLEET_HAIL(27),
TORNADO(28),
TROPICAL_STORM_HURRICANE(29),
WINDY_BREEZY(30),
WINTRY_MIX_RAIN_SNOW(31);
companion object {
fun fromInt(value: Int) = values().firstOrNull { it.id == value }
}
}
override fun toString(): String {
val unit = if (useCelsius) "C" else "F"
return "$state (\"$description\") $temperature°$unit"
}
}

View File

@@ -48,7 +48,7 @@ import com.android.systemui.plugins.ClockTickRate
import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.plugins.log.LogLevel.DEBUG
import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.plugins.Weather import com.android.systemui.plugins.WeatherData
import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
@@ -291,12 +291,10 @@ constructor(
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context)) clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
} }
override fun onWeatherDataChanged(data: Weather?) { override fun onWeatherDataChanged(data: WeatherData) {
if (data != null) {
clock?.events?.onWeatherDataChanged(data) clock?.events?.onWeatherDataChanged(data)
} }
} }
}
fun registerListeners(parent: View) { fun registerListeners(parent: View) {
if (isRegistered) { if (isRegistered) {

View File

@@ -148,7 +148,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger; import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.log.SessionTracker; import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.Weather; import com.android.systemui.plugins.WeatherData;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -3286,12 +3286,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/** /**
* @param data the weather data (temp, conditions, unit) for weather clock to use * @param data the weather data (temp, conditions, unit) for weather clock to use
*/ */
public void sendWeatherData(Weather data) { public void sendWeatherData(WeatherData data) {
mHandler.post(()-> { mHandler.post(()-> {
handleWeatherDataUpdate(data); }); handleWeatherDataUpdate(data); });
} }
private void handleWeatherDataUpdate(Weather data) { private void handleWeatherDataUpdate(WeatherData data) {
Assert.isMainThread(); Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) { for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();

View File

@@ -23,7 +23,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.plugins.Weather; import com.android.systemui.plugins.WeatherData;
import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.KeyguardIndicationController;
import java.util.TimeZone; import java.util.TimeZone;
@@ -61,7 +61,7 @@ public class KeyguardUpdateMonitorCallback {
/** /**
* Called when receive new weather data. * Called when receive new weather data.
*/ */
public void onWeatherDataChanged(Weather data) { } public void onWeatherDataChanged(WeatherData data) { }
/** /**
* Called when the carrier PLMN or SPN changes. * Called when the carrier PLMN or SPN changes.

View File

@@ -52,18 +52,19 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.WeatherData
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.shared.regionsampling.UpdateColorCallback import com.android.systemui.shared.regionsampling.UpdateColorCallback
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.plugins.Weather
import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter import java.io.PrintWriter
import java.time.Instant import java.time.Instant
import java.util.Optional import java.util.Optional
@@ -81,6 +82,7 @@ constructor(
private val smartspaceManager: SmartspaceManager, private val smartspaceManager: SmartspaceManager,
private val activityStarter: ActivityStarter, private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager, private val falsingManager: FalsingManager,
private val systemClock: SystemClock,
private val secureSettings: SecureSettings, private val secureSettings: SecureSettings,
private val userTracker: UserTracker, private val userTracker: UserTracker,
private val contentResolver: ContentResolver, private val contentResolver: ContentResolver,
@@ -152,6 +154,18 @@ constructor(
// The weather data plugin takes unfiltered targets and performs the filtering internally. // The weather data plugin takes unfiltered targets and performs the filtering internally.
weatherPlugin?.onTargetsAvailable(targets) weatherPlugin?.onTargetsAvailable(targets)
val now = Instant.ofEpochMilli(systemClock.currentTimeMillis())
val weatherTarget = targets.find { t ->
t.featureType == SmartspaceTarget.FEATURE_WEATHER &&
now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) &&
now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis))
}
if (weatherTarget != null) {
val weatherData = WeatherData.fromBundle(weatherTarget.baseAction.extras)
if (weatherData != null) {
keyguardUpdateMonitor.sendWeatherData(weatherData)
}
}
val filteredTargets = targets.filter(::filterSmartspaceTarget) val filteredTargets = targets.filter(::filterSmartspaceTarget)
plugin?.onTargetsAvailable(filteredTargets) plugin?.onTargetsAvailable(filteredTargets)
@@ -173,17 +187,6 @@ constructor(
} }
isContentUpdatedOnce = true isContentUpdatedOnce = true
} }
val now = Instant.now()
val weatherTarget = targets.find { t ->
t.featureType == SmartspaceTarget.FEATURE_WEATHER &&
now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) &&
now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis))
}
if (weatherTarget != null) {
val weatherData = Weather.fromBundle(weatherTarget.baseAction.extras)
keyguardUpdateMonitor.sendWeatherData(weatherData)
}
} }
private val userTrackerCallback = object : UserTracker.Callback { private val userTrackerCallback = object : UserTracker.Callback {

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.lockscreen package com.android.systemui.statusbar.lockscreen
import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceManager import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession import android.app.smartspace.SmartspaceSession
import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
@@ -26,6 +27,7 @@ import android.content.pm.UserInfo
import android.database.ContentObserver import android.database.ContentObserver
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.UserHandle import android.os.UserHandle
import android.provider.Settings import android.provider.Settings
@@ -43,6 +45,7 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.WeatherData
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
@@ -54,6 +57,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP
import com.android.systemui.util.concurrency.FakeExecution import com.android.systemui.util.concurrency.FakeExecution
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
@@ -69,6 +73,7 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.spy import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.util.Optional import java.util.Optional
@@ -76,6 +81,13 @@ import java.util.concurrent.Executor
@SmallTest @SmallTest
class LockscreenSmartspaceControllerTest : SysuiTestCase() { class LockscreenSmartspaceControllerTest : SysuiTestCase() {
companion object {
const val SMARTSPACE_TIME_TOO_EARLY = 1000L
const val SMARTSPACE_TIME_JUST_RIGHT = 4000L
const val SMARTSPACE_TIME_TOO_LATE = 9000L
const val SMARTSPACE_CREATION_TIME = 1234L
const val SMARTSPACE_EXPIRY_TIME = 5678L
}
@Mock @Mock
private lateinit var featureFlags: FeatureFlags private lateinit var featureFlags: FeatureFlags
@Mock @Mock
@@ -224,6 +236,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
smartspaceManager, smartspaceManager,
activityStarter, activityStarter,
falsingManager, falsingManager,
clock,
secureSettings, secureSettings,
userTracker, userTracker,
contentResolver, contentResolver,
@@ -528,6 +541,190 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(datePlugin, never()).onTargetsAvailable(any()) verify(datePlugin, never()).onTargetsAvailable(any())
} }
@Test
fun testSessionListener_ifWeatherExtraMissing_thenWeatherDataNotSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeTarget(1, userHandlePrimary, isSensitive = true),
makeTarget(2, userHandlePrimary, featureType = SmartspaceTarget.FEATURE_WEATHER)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor, times(0)).sendWeatherData(any())
}
@Test
fun testSessionListener_ifWeatherExtraIsMissingValues_thenWeatherDataNotSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeTarget(1, userHandlePrimary, isSensitive = true),
makeWeatherTargetWithExtras(
id = 2,
userHandle = userHandlePrimary,
description = null,
state = WeatherData.WeatherStateIcon.SUNNY.id,
temperature = "32",
useCelsius = null)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor, times(0)).sendWeatherData(any())
}
@Test
fun testSessionListener_ifTooEarly_thenWeatherDataNotSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_TOO_EARLY)
// WHEN we receive a list of targets
val targets = listOf(
makeWeatherTargetWithExtras(
id = 1,
userHandle = userHandleManaged,
description = "Sunny",
state = WeatherData.WeatherStateIcon.SUNNY.id,
temperature = "32",
useCelsius = false)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor, times(0)).sendWeatherData(any())
}
@Test
fun testSessionListener_ifOnTime_thenWeatherDataSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeWeatherTargetWithExtras(
id = 1,
userHandle = userHandleManaged,
description = "Snow Showers",
state = WeatherData.WeatherStateIcon.SNOW_SHOWERS_SNOW.id,
temperature = "-1",
useCelsius = false)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
w.description == "Snow Showers" &&
w.state == WeatherData.WeatherStateIcon.SNOW_SHOWERS_SNOW &&
w.temperature == -1 && !w.useCelsius
})
}
@Test
fun testSessionListener_ifTooLate_thenWeatherDataNotSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_TOO_LATE)
// WHEN we receive a list of targets
val targets = listOf(
makeWeatherTargetWithExtras(
id = 1,
userHandle = userHandleManaged,
description = "Sunny",
state = WeatherData.WeatherStateIcon.SUNNY.id,
temperature = "72",
useCelsius = false)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor, times(0)).sendWeatherData(any())
}
@Test
fun testSessionListener_onlyFirstWeatherDataSent() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeWeatherTargetWithExtras(
id = 1,
userHandle = userHandleManaged,
description = "Sunny",
state = WeatherData.WeatherStateIcon.SUNNY.id,
temperature = "72",
useCelsius = false),
makeWeatherTargetWithExtras(
id = 2,
userHandle = userHandleManaged,
description = "Showers",
state = WeatherData.WeatherStateIcon.SHOWERS_RAIN.id,
temperature = "62",
useCelsius = true)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
w.description == "Sunny" &&
w.state == WeatherData.WeatherStateIcon.SUNNY &&
w.temperature == 72 && !w.useCelsius
})
}
@Test
fun testSessionListener_ifDecouplingEnabled_weatherDataUpdates() {
`when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeTarget(1, userHandlePrimary, isSensitive = true),
makeTarget(2, userHandlePrimary),
makeTarget(3, userHandleManaged),
makeWeatherTargetWithExtras(
id = 4,
userHandle = userHandlePrimary,
description = "Flurries",
state = WeatherData.WeatherStateIcon.FLURRIES.id,
temperature = "0",
useCelsius = true)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
w.description == "Flurries" &&
w.state == WeatherData.WeatherStateIcon.FLURRIES &&
w.temperature == 0 && w.useCelsius
})
}
@Test
fun testSessionListener_ifDecouplingDisabled_weatherDataUpdates() {
`when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
// WHEN we receive a list of targets
val targets = listOf(
makeWeatherTargetWithExtras(
id = 1,
userHandle = userHandlePrimary,
description = "Sunny",
state = WeatherData.WeatherStateIcon.SUNNY.id,
temperature = "32",
useCelsius = false),
makeTarget(2, userHandlePrimary, isSensitive = true)
)
sessionListener.onTargetsAvailable(targets)
verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
w.description == "Sunny" &&
w.state == WeatherData.WeatherStateIcon.SUNNY &&
w.temperature == 32 && !w.useCelsius
})
}
@Test @Test
fun testSettingsAreReloaded() { fun testSettingsAreReloaded() {
// GIVEN a connected session where the privacy settings later flip to false // GIVEN a connected session where the privacy settings later flip to false
@@ -740,7 +937,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
return userInfo return userInfo
} }
fun makeTarget( private fun makeTarget(
id: Int, id: Int,
userHandle: UserHandle, userHandle: UserHandle,
isSensitive: Boolean = false, isSensitive: Boolean = false,
@@ -755,6 +952,38 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
.build() .build()
} }
private fun makeWeatherTargetWithExtras(
id: Int,
userHandle: UserHandle,
description: String?,
state: Int?,
temperature: String?,
useCelsius: Boolean?
): SmartspaceTarget {
val mockWeatherBundle = mock(Bundle::class.java).apply {
`when`(getString(WeatherData.DESCRIPTION_KEY)).thenReturn(description)
if (state != null)
`when`(getInt(eq(WeatherData.STATE_KEY), any())).thenReturn(state)
`when`(getString(WeatherData.TEMPERATURE_KEY)).thenReturn(temperature)
`when`(containsKey(WeatherData.USE_CELSIUS_KEY)).thenReturn(useCelsius != null)
if (useCelsius != null)
`when`(getBoolean(WeatherData.USE_CELSIUS_KEY)).thenReturn(useCelsius)
}
val mockBaseAction = mock(SmartspaceAction::class.java)
`when`(mockBaseAction.extras).thenReturn(mockWeatherBundle)
return SmartspaceTarget.Builder(
"targetWithWeatherExtras$id",
ComponentName("testpackage", "testclass$id"),
userHandle)
.setSensitive(false)
.setFeatureType(SmartspaceTarget.FEATURE_WEATHER)
.setBaseAction(mockBaseAction)
.setExpiryTimeMillis(SMARTSPACE_EXPIRY_TIME)
.setCreationTimeMillis(SMARTSPACE_CREATION_TIME)
.build()
}
private fun setAllowPrivateNotifications(user: UserHandle, value: Boolean) { private fun setAllowPrivateNotifications(user: UserHandle, value: Boolean) {
`when`(secureSettings.getIntForUser( `when`(secureSettings.getIntForUser(
eq(PRIVATE_LOCKSCREEN_SETTING), eq(PRIVATE_LOCKSCREEN_SETTING),