Implement air quality dream complication

Fixes: 213906528
Test: atest AirQualityColorPickerTest
Test: atest DreamAirQualityComplicationTest
Change-Id: I31f289c8bf223dde9464ad5171f68ada04f6ee89
This commit is contained in:
Lucas Silva
2022-06-13 11:16:36 -04:00
parent 366cdaf821
commit ee76de8f4a
14 changed files with 706 additions and 17 deletions

View File

@@ -0,0 +1,20 @@
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/dream_overlay_aqi_unknown" />
<corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
</shape>

View File

@@ -0,0 +1,26 @@
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/aqi_view"
style="@style/clock_subtitle"
android:visibility="gone"
android:background="@drawable/dream_aqi_badge_bg"
android:paddingHorizontal="@dimen/dream_aqi_badge_padding_horizontal"
android:paddingVertical="@dimen/dream_aqi_badge_padding_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

View File

@@ -233,4 +233,13 @@
<color name="connected_network_secondary_color">#41493D</color>
<color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
<!-- Air Quality -->
<color name="dream_overlay_aqi_good">#689F38</color>
<color name="dream_overlay_aqi_moderate">#FBC02D</color>
<color name="dream_overlay_aqi_unhealthy_sensitive">#F57C00</color>
<color name="dream_overlay_aqi_unhealthy">#C53929</color>
<color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
<color name="dream_overlay_aqi_hazardous">#880E4F</color>
<color name="dream_overlay_aqi_unknown">#BDC1C6</color>
</resources>

View File

@@ -727,4 +727,25 @@
<item>com.android.keyguard</item>
<item>com.android.systemui</item>
</string-array>
<!-- The thresholds which determine the color used by the AQI dream overlay.
NOTE: This must always be kept sorted from low to high -->
<integer-array name="config_dreamAqiThresholds">
<item>-1</item>
<item>50</item>
<item>100</item>
<item>150</item>
<item>200</item>
<item>300</item>
</integer-array>
<!-- The color values which correspond to the thresholds above -->
<integer-array name="config_dreamAqiColorValues">
<item>@color/dream_overlay_aqi_good</item>
<item>@color/dream_overlay_aqi_moderate</item>
<item>@color/dream_overlay_aqi_unhealthy_sensitive</item>
<item>@color/dream_overlay_aqi_unhealthy</item>
<item>@color/dream_overlay_aqi_very_unhealthy</item>
<item>@color/dream_overlay_aqi_hazardous</item>
</integer-array>
</resources>

View File

@@ -1494,6 +1494,10 @@
<dimen name="dream_overlay_y_offset">80dp</dimen>
<dimen name="dream_aqi_badge_corner_radius">28dp</dimen>
<dimen name="dream_aqi_badge_padding_vertical">6dp</dimen>
<dimen name="dream_aqi_badge_padding_horizontal">16dp</dimen>
<dimen name="status_view_margin_horizontal">0dp</dimen>
<!-- Media output broadcast dialog QR code picture size -->

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.dreams.complication;
import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_DEFAULT;
import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_THRESHOLDS;
import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_VALUES;
import android.util.Log;
import androidx.annotation.ColorInt;
import javax.inject.Inject;
import javax.inject.Named;
final class AirQualityColorPicker {
private static final String TAG = "AirQualityColorPicker";
private final int[] mThresholds;
private final int[] mColorValues;
private final int mDefaultColor;
@Inject
AirQualityColorPicker(@Named(DREAM_AQI_COLOR_THRESHOLDS) int[] thresholds,
@Named(DREAM_AQI_COLOR_VALUES) int[] colorValues,
@Named(DREAM_AQI_COLOR_DEFAULT) @ColorInt int defaultColor) {
mThresholds = thresholds;
mColorValues = colorValues;
mDefaultColor = defaultColor;
}
@ColorInt
int getColorForValue(String aqiString) {
int size = mThresholds.length;
if (mThresholds.length != mColorValues.length) {
size = Math.min(mThresholds.length, mColorValues.length);
Log.e(TAG,
"Threshold size ("
+ mThresholds.length + ") does not match color value size ("
+ mColorValues.length
+ "). Taking the minimum, some values may be ignored.");
}
try {
final int value = Integer.parseInt(aqiString.replaceAll("[^0-9]", ""));
for (int i = size - 1; i >= 0; i--) {
if (value > mThresholds[i]) {
return mColorValues[i];
}
}
Log.e(TAG, "No matching AQI color for value: " + value);
} catch (NumberFormatException e) {
Log.e(TAG, "Could not read AQI value from:" + aqiString);
}
return mDefaultColor;
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.dreams.complication;
import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_LAYOUT_PARAMS;
import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_VIEW;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
import javax.inject.Named;
/**
* Air quality complication which produces view holder responsible for showing AQI over dreams.
*/
public class DreamAirQualityComplication implements Complication {
// TODO(b/236024839): Move to SmartspaceTarget
public static final int FEATURE_AIR_QUALITY = 46;
private final DreamAirQualityComplicationComponent.Factory mComponentFactory;
@Inject
public DreamAirQualityComplication(
DreamAirQualityComplicationComponent.Factory componentFactory) {
mComponentFactory = componentFactory;
}
@Override
public int getRequiredTypeAvailability() {
return COMPLICATION_TYPE_AIR_QUALITY;
}
@Override
public ViewHolder createView(ComplicationViewModel model) {
return mComponentFactory.create().getViewHolder();
}
/**
* {@link CoreStartable} for registering {@link DreamAirQualityComplication} with SystemUI.
*/
public static class Registrant extends CoreStartable {
private final DreamOverlayStateController mDreamOverlayStateController;
private final DreamAirQualityComplication mComplication;
/**
* Default constructor to register {@link DreamAirQualityComplication}.
*/
@Inject
public Registrant(Context context,
DreamOverlayStateController dreamOverlayStateController,
DreamAirQualityComplication complication) {
super(context);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = complication;
}
@Override
public void start() {
// TODO(b/221500478): Only add complication once we have data to show.
mDreamOverlayStateController.addComplication(mComplication);
}
}
/**
* ViewHolder to contain value/logic associated with the AQI complication view.
*/
public static class DreamAirQualityViewHolder implements ViewHolder {
private final TextView mView;
private final DreamAirQualityViewController mController;
private final ComplicationLayoutParams mLayoutParams;
@Inject
DreamAirQualityViewHolder(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view,
DreamAirQualityViewController controller,
@Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS)
ComplicationLayoutParams layoutParams) {
mView = view;
mLayoutParams = layoutParams;
mController = controller;
mController.init();
}
@Override
public View getView() {
return mView;
}
@Override
public ComplicationLayoutParams getLayoutParams() {
return mLayoutParams;
}
}
static class DreamAirQualityViewController extends ViewController<TextView> {
private final DreamSmartspaceController mSmartspaceController;
private final String mSmartspaceTrampolineComponent;
private final ActivityStarter mActivityStarter;
private final AirQualityColorPicker mAirQualityColorPicker;
private final SmartspaceTargetListener mSmartspaceTargetListener = targets -> {
final SmartspaceTarget target = targets.stream()
.filter(t -> t instanceof SmartspaceTarget)
.map(t -> (SmartspaceTarget) t)
.filter(t -> t.getFeatureType() == FEATURE_AIR_QUALITY)
.findFirst()
.orElse(null);
updateView(target);
};
@Inject
DreamAirQualityViewController(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view,
DreamSmartspaceController smartspaceController,
@Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
String smartspaceTrampolineComponent,
ActivityStarter activityStarter,
AirQualityColorPicker airQualityColorPicker) {
super(view);
mSmartspaceController = smartspaceController;
mSmartspaceTrampolineComponent = smartspaceTrampolineComponent;
mActivityStarter = activityStarter;
mAirQualityColorPicker = airQualityColorPicker;
}
@Override
protected void onViewAttached() {
mSmartspaceController.addUnfilteredListener(mSmartspaceTargetListener);
}
@Override
protected void onViewDetached() {
mSmartspaceController.removeUnfilteredListener(mSmartspaceTargetListener);
}
private void updateView(@Nullable SmartspaceTarget target) {
final SmartspaceAction headerAction = target == null ? null : target.getHeaderAction();
if (headerAction == null || TextUtils.isEmpty(headerAction.getTitle())) {
mView.setVisibility(View.GONE);
return;
}
mView.setVisibility(View.VISIBLE);
final String airQuality = headerAction.getTitle().toString();
mView.setText(airQuality);
final Drawable background = mView.getBackground().mutate();
final int color = mAirQualityColorPicker.getColorForValue(airQuality);
if (background instanceof ShapeDrawable) {
((ShapeDrawable) background).getPaint().setColor(color);
} else if (background instanceof GradientDrawable) {
((GradientDrawable) background).setColor(color);
}
mView.setBackground(background);
final Intent intent = headerAction.getIntent();
if (intent != null && intent.getComponent() != null
&& intent.getComponent().getClassName().equals(
mSmartspaceTrampolineComponent)) {
mView.setOnClickListener(v -> {
mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0);
});
}
}
}
}

View File

@@ -18,7 +18,7 @@ package com.android.systemui.dreams.complication;
import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS;
import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW;
import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.dreams.complication.dagger;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationLayoutParams;
import com.android.systemui.dreams.complication.DreamAirQualityComplication;
import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewHolder;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import javax.inject.Named;
import javax.inject.Scope;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
/**
* Component responsible for generating dependencies for the {@link DreamAirQualityComplication},
* such as the layout details.
*/
@Subcomponent(modules = {
DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.class,
})
@DreamAirQualityComplicationComponent.DreamAirQualityComplicationScope
public interface DreamAirQualityComplicationComponent {
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Scope
@interface DreamAirQualityComplicationScope {
}
/**
* Generates {@link DreamAirQualityComplicationComponent}.
*/
@Subcomponent.Factory
interface Factory {
DreamAirQualityComplicationComponent create();
}
/**
* Creates {@link DreamAirQualityViewHolder}.
*/
DreamAirQualityViewHolder getViewHolder();
/**
* Scoped values for {@link DreamAirQualityComplicationComponent}.
*/
@Module
interface DreamAirQualityComplicationModule {
String DREAM_AQI_COMPLICATION_VIEW = "aqi_complication_view";
String DREAM_AQI_COMPLICATION_LAYOUT_PARAMS = "aqi_complication_layout_params";
String DREAM_AQI_COLOR_THRESHOLDS = "aqi_color_thresholds";
String DREAM_AQI_COLOR_VALUES = "aqi_color_values";
String DREAM_AQI_COLOR_DEFAULT = "aqi_color_default";
// Order weight of insert into parent container
int INSERT_ORDER_WEIGHT = 1;
/**
* Provides the complication view.
*/
@Provides
@DreamAirQualityComplicationScope
@Named(DREAM_AQI_COMPLICATION_VIEW)
static TextView provideComplicationView(LayoutInflater layoutInflater) {
return Objects.requireNonNull((TextView)
layoutInflater.inflate(R.layout.dream_overlay_complication_aqi,
null, false),
"R.layout.dream_overlay_complication_aqi did not properly inflated");
}
/**
* Provides the layout parameters for the complication view.
*/
@Provides
@DreamAirQualityComplicationScope
@Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS)
static ComplicationLayoutParams provideLayoutParams() {
return new ComplicationLayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT,
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
}
@Provides
@DreamAirQualityComplicationScope
@Named(DREAM_AQI_COLOR_THRESHOLDS)
static int[] provideAqiColorThresholds(@Main Resources resources) {
return resources.getIntArray(R.array.config_dreamAqiThresholds);
}
@Provides
@DreamAirQualityComplicationScope
@Named(DREAM_AQI_COLOR_VALUES)
static int[] provideAqiColorValues(@Main Resources resources) {
return resources.getIntArray(R.array.config_dreamAqiColorValues);
}
@Provides
@DreamAirQualityComplicationScope
@Named(DREAM_AQI_COLOR_DEFAULT)
@ColorInt
static int provideDefaultAqiColor(Context context) {
return context.getColor(R.color.dream_overlay_aqi_unknown);
}
}
}

View File

@@ -41,7 +41,7 @@ public interface DreamClockDateComplicationModule {
"clock_date_complication_layout_params";
// Order weight of insert into parent container
//TODO(b/217199227): move to a single location.
int INSERT_ORDER_WEIGHT = 2;
int INSERT_ORDER_WEIGHT = 3;
/**
* Provides the complication view.
@@ -66,6 +66,6 @@ public interface DreamClockDateComplicationModule {
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
INSERT_ORDER_WEIGHT);
INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
}
}

View File

@@ -19,7 +19,6 @@ package com.android.systemui.dreams.complication.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -77,9 +76,8 @@ public interface DreamWeatherComplicationComponent {
String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view";
String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS =
"weather_complication_layout_params";
String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity";
// Order weight of insert into parent container
int INSERT_ORDER_WEIGHT = 1;
int INSERT_ORDER_WEIGHT = 2;
/**
* Provides the complication view.
@@ -106,17 +104,7 @@ public interface DreamWeatherComplicationComponent {
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
INSERT_ORDER_WEIGHT);
}
/**
* Provides the smartspace trampoline activity component.
*/
@Provides
@DreamWeatherComplicationScope
@Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
static String provideSmartspaceTrampolineActivityComponent(Context context) {
return context.getString(R.string.config_smartspaceTrampolineActivityComponent);
INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
}
}
}

View File

@@ -16,9 +16,15 @@
package com.android.systemui.dreams.complication.dagger;
import android.content.Context;
import com.android.systemui.R;
import com.android.systemui.dagger.SystemUIBinder;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
/**
* Module for all components with corresponding dream layer complications registered in
@@ -30,6 +36,17 @@ import dagger.Module;
},
subcomponents = {
DreamWeatherComplicationComponent.class,
DreamAirQualityComplicationComponent.class,
})
public interface RegisteredComplicationsModule {
String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity";
/**
* Provides the smartspace trampoline activity component.
*/
@Provides
@Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT)
static String provideSmartspaceTrampolineActivityComponent(Context context) {
return context.getString(R.string.config_smartspaceTrampolineActivityComponent);
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.dreams.complication;
import static com.google.common.truth.Truth.assertThat;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class AirQualityColorPickerTest extends SysuiTestCase {
private static final int DEFAULT_COLOR = 0;
private static final int MOCK_COLOR_1 = 1;
private static final int MOCK_COLOR_2 = 2;
private static final int MOCK_COLOR_3 = 3;
private static final int MOCK_COLOR_4 = 4;
private static final int MOCK_COLOR_5 = 5;
private static final int[] MOCK_THRESHOLDS = {-1, 100, 200, 201, 500};
private static final int[] MOCK_COLORS =
{MOCK_COLOR_1, MOCK_COLOR_2, MOCK_COLOR_3, MOCK_COLOR_4, MOCK_COLOR_5};
private static final int[] EMPTY_ARRAY = {};
@Test
public void testEmptyThresholds() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
EMPTY_ARRAY,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR);
}
@Test
public void testEmptyColors() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
EMPTY_ARRAY,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR);
}
@Test
public void testEmptyAqiString() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("")).isEqualTo(DEFAULT_COLOR);
}
@Test
public void testInvalidAqiString() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("invalid")).isEqualTo(DEFAULT_COLOR);
}
@Test
public void testZeroAirQuality() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("0 AQI")).isEqualTo(MOCK_COLOR_1);
}
@Test
public void testVeryLargeAirQuality() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("100000 AQI")).isEqualTo(MOCK_COLOR_5);
}
@Test
public void testAirQuality200() {
final AirQualityColorPicker colorPicker = new AirQualityColorPicker(
MOCK_THRESHOLDS,
MOCK_COLORS,
DEFAULT_COLOR);
assertThat(colorPicker.getColorForValue("200 AQI")).isEqualTo(MOCK_COLOR_2);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.dreams.complication;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
import android.widget.TextView;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewController;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.ActivityStarter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamAirQualityComplicationTest extends SysuiTestCase {
private static final String TRAMPOLINE_COMPONENT = "TestComponent";
@Mock
private DreamSmartspaceController mDreamSmartspaceController;
@Mock
private DreamOverlayStateController mDreamOverlayStateController;
@Mock
private DreamAirQualityComplication mComplication;
@Mock
private AirQualityColorPicker mColorPicker;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
/**
* Ensures {@link DreamAirQualityComplication} is registered.
*/
@Test
public void testComplicationRegistered() {
final DreamAirQualityComplication.Registrant registrant =
new DreamAirQualityComplication.Registrant(
mContext,
mDreamOverlayStateController,
mComplication);
registrant.start();
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
}
@Test
public void testGetUnfilteredTargets() {
final DreamAirQualityViewController controller =
new DreamAirQualityViewController(
mock(TextView.class),
mDreamSmartspaceController,
TRAMPOLINE_COMPONENT,
mock(ActivityStarter.class),
mColorPicker);
controller.onViewAttached();
verify(mDreamSmartspaceController).addUnfilteredListener(any());
controller.onViewDetached();
verify(mDreamSmartspaceController).removeUnfilteredListener(any());
}
}