diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/res/drawable/car_ic_apps.xml index a8d8a2f241f6c..e028a0ed45c94 100644 --- a/packages/CarSystemUI/res/drawable/car_ic_apps.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_apps.xml @@ -15,11 +15,11 @@ ~ limitations under the License --> - + android:viewportHeight="44"> + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml index 2a4e91aa3cd9e..9504e61e53e42 100644 --- a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml @@ -15,10 +15,10 @@ ~ limitations under the License --> + android:viewportHeight="44"> diff --git a/packages/CarSystemUI/res/drawable/car_ic_home.xml b/packages/CarSystemUI/res/drawable/car_ic_home.xml new file mode 100644 index 0000000000000..c78f0edd5594c --- /dev/null +++ b/packages/CarSystemUI/res/drawable/car_ic_home.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml new file mode 100644 index 0000000000000..16192df866760 --- /dev/null +++ b/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml index bdc44b38a1762..55c968eacc4d2 100644 --- a/packages/CarSystemUI/res/drawable/car_ic_hvac.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml @@ -1,3 +1,4 @@ + - - - - - - - - - \ No newline at end of file + android:width="@dimen/system_bar_icon_drawing_size" + android:height="@dimen/system_bar_icon_drawing_size" + android:viewportWidth="24" + android:viewportHeight="24"> + + diff --git a/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml new file mode 100644 index 0000000000000..817b7148ecdd9 --- /dev/null +++ b/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/res/drawable/car_ic_notification.xml index 9d9ad0fdf9c59..aabf9161c11fe 100644 --- a/packages/CarSystemUI/res/drawable/car_ic_notification.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_notification.xml @@ -15,10 +15,10 @@ ~ limitations under the License --> + android:width="@dimen/system_bar_icon_drawing_size" + android:height="@dimen/system_bar_icon_drawing_size" + android:viewportWidth="44" + android:viewportHeight="44"> diff --git a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml new file mode 100644 index 0000000000000..1195d05da2286 --- /dev/null +++ b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml b/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml new file mode 100644 index 0000000000000..469ac91073f99 --- /dev/null +++ b/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/hvac_increase_button.xml b/packages/CarSystemUI/res/drawable/hvac_increase_button.xml new file mode 100644 index 0000000000000..a3fca2233dddd --- /dev/null +++ b/packages/CarSystemUI/res/drawable/hvac_increase_button.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml index 546b1a894e6ad..71fcc5302d75c 100644 --- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml +++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml @@ -15,11 +15,11 @@ ~ limitations under the License --> - + \ No newline at end of file diff --git a/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml b/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml new file mode 100644 index 0000000000000..d19740932aa43 --- /dev/null +++ b/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index a49a6373a252b..b07dde582e5f6 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -1,6 +1,6 @@ - - + + + android:layoutDirection="ltr"> - - + android:layout_alignParentStart="true" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"> + + + + + + + + + + + + + + + + + + + + + + - - + android:layout_alignParentEnd="true" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"> - - - - - - - - - - - - - - - - - - - + + + - \ No newline at end of file diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index cdc29eec21cde..af8482a8c6a51 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -31,39 +31,44 @@ android:layoutDirection="ltr"> - - + systemui:icon="@null" + systemui:intent="intent:#Intent;component=com.android.car.settings/.users.UserSwitcherActivity;launchFlags=0x24000000;end" + > + + + + + @@ -94,10 +100,9 @@ android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_centerHorizontal="true" + android:layout_alignParentEnd="true" android:layout_centerVertical="true" - android:layout_toEndOf="@+id/clock_container" - android:paddingStart="@*android:dimen/car_padding_1" + android:paddingEnd="@*android:dimen/car_padding_1" android:gravity="center_vertical" android:orientation="horizontal" > @@ -107,46 +112,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingStart="4dp" android:gravity="center_vertical" /> - - - - - - - diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml index 9634950e47488..d9c149106451a 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml @@ -55,10 +55,6 @@ android:minEms="4" android:textAppearance="@style/TextAppearance.CarStatus" systemui:hvacAreaId="49" - systemui:hvacMaxText="@string/hvac_max_text" - systemui:hvacMaxValue="@dimen/hvac_max_value" - systemui:hvacMinText="@string/hvac_min_text" - systemui:hvacMinValue="@dimen/hvac_min_value" systemui:hvacPivotOffset="60dp" systemui:hvacPropertyId="358614275" systemui:hvacTempFormat="%.0f\u00B0" @@ -133,10 +129,6 @@ android:minEms="4" android:textAppearance="@style/TextAppearance.CarStatus" systemui:hvacAreaId="68" - systemui:hvacMaxText="@string/hvac_max_text" - systemui:hvacMaxValue="@dimen/hvac_max_value" - systemui:hvacMinText="@string/hvac_min_text" - systemui:hvacMinValue="@dimen/hvac_min_value" systemui:hvacPivotOffset="60dp" systemui:hvacPropertyId="358614275" systemui:hvacTempFormat="%.0f\u00B0" diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml index ab94265935359..1e15affcbf489 100644 --- a/packages/CarSystemUI/res/values/colors.xml +++ b/packages/CarSystemUI/res/values/colors.xml @@ -31,6 +31,9 @@ @*android:color/car_grey_50 #ff172026 + + #ffffff + #ffffff #33000000 @color/status_bar_background_color diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 8359dac6a30fe..f02a8e7648c0f 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -46,16 +46,23 @@ in frameworks/base/core package and thus will have no effect if set here. See car_product overlay for car specific defaults--> - 36dp - 36dp + 16dp + 36dp + 64dp + 44dp + 1.75 @*android:dimen/car_primary_icon_size + 16dp + 56sp + 8dp + 76dp - 0 - 126 + 0 + 126 diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml index 67fd5bb685211..fbdb5167fadee 100644 --- a/packages/CarSystemUI/res/values/strings.xml +++ b/packages/CarSystemUI/res/values/strings.xml @@ -16,6 +16,8 @@ --> + + %.0f\u00B0 Min diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml index e76373d4a4f7b..0db17ac42a779 100644 --- a/packages/CarSystemUI/res/values/styles.xml +++ b/packages/CarSystemUI/res/values/styles.xml @@ -25,21 +25,29 @@ 22dp - + + \ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java new file mode 100644 index 0000000000000..4cac4456789d7 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 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.car.hvac; + +import static com.android.systemui.car.hvac.HvacController.convertToCelsius; +import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.systemui.R; + +/** + * Displays temperature with a button to decrease and a button to increase on either side. + * Properties configured in the XML: + * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + */ +public class AdjustableTemperatureView extends LinearLayout implements TemperatureView { + + private HvacController mHvacController; + private float mCurrentTempC; + private TextView mTempTextView; + private boolean mDisplayInFahrenheit = false; + + private final float mMinTempC; + private final float mMaxTempC; + private final int mAreaId; + private final String mTempFormat; + + public AdjustableTemperatureView(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView); + mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1); + + LayoutInflater.from(context).inflate(R.layout.adjustable_temperature_view, /* root= */this); + mTempFormat = getResources().getString(R.string.hvac_temperature_format); + mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius); + mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius); + initializeButtons(); + } + + @Override + public void setHvacController(HvacController controller) { + mHvacController = controller; + } + + @Override + public void setTemperatureView(float tempC) { + if (tempC > mMaxTempC || tempC < mMinTempC) { + return; + } + if (mTempTextView == null) { + mTempTextView = findViewById(R.id.hvac_temperature_text); + } + mTempTextView.setText(String.format(mTempFormat, + mDisplayInFahrenheit ? convertToFahrenheit(tempC) : tempC)); + mCurrentTempC = tempC; + } + + @Override + public void setDisplayInFahrenheit(boolean displayFahrenheit) { + mDisplayInFahrenheit = displayFahrenheit; + setTemperatureView(mCurrentTempC); + } + + @Override + public int getAreaId() { + return mAreaId; + } + + private void initializeButtons() { + findViewById(R.id.hvac_decrease_button).setOnClickListener(v -> { + float newTemp = mDisplayInFahrenheit ? convertToCelsius( + convertToFahrenheit(mCurrentTempC) - 1) : (mCurrentTempC - 1); + setTemperature(newTemp, mAreaId); + }); + + findViewById(R.id.hvac_increase_button).setOnClickListener(v -> { + float newTemp = mDisplayInFahrenheit ? convertToCelsius( + convertToFahrenheit(mCurrentTempC) + 1) : (mCurrentTempC + 1); + setTemperature(newTemp, mAreaId); + }); + } + + private void setTemperature(float tempC, int zone) { + if (tempC < mMaxTempC && tempC > mMinTempC && mHvacController != null) { + mHvacController.setTemperature(tempC, zone); + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java index a7294317f46c3..567baa91cb59b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java @@ -16,6 +16,8 @@ package com.android.systemui.car.hvac; +import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit; + import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.content.Context; @@ -40,9 +42,7 @@ import com.android.systemui.R; * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in * the XML. * XML properties: - * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) - * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) - * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol) + * hvacAreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1) * hvacOrientaion = Example: left */ public class AnimatedTemperatureView extends FrameLayout implements TemperatureView { @@ -84,7 +84,6 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV } private final int mAreaId; - private final int mPropertyId; private final int mPivotOffset; private final int mGravity; private final int mTextAppearanceRes; @@ -100,12 +99,13 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV private final TemperatureTextAnimator mTextAnimator; boolean mDisplayInFahrenheit = false; + private HvacController mHvacController; + public AnimatedTemperatureView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AnimatedTemperatureView); mAreaId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacAreaId, -1); - mPropertyId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacPropertyId, -1); mPivotOffset = typedArray.getDimensionPixelOffset( R.styleable.AnimatedTemperatureView_hvacPivotOffset, 0); @@ -115,11 +115,8 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV typedArray.getResourceId(R.styleable.AnimatedTemperatureView_android_textAppearance, 0); mMinEms = typedArray.getInteger(R.styleable.AnimatedTemperatureView_android_minEms, 0); - mMinValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMinValue, - Float.NaN); - mMaxValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMaxValue, - Float.NaN); - + mMinValue = getResources().getFloat(R.dimen.hvac_min_value_celsius); + mMaxValue = getResources().getFloat(R.dimen.hvac_max_value_celsius); mPaddingRect = new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); @@ -138,15 +135,10 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV mBackgroundAnimator = new TemperatureBackgroundAnimator(this, background); - - String format = typedArray.getString(R.styleable.AnimatedTemperatureView_hvacTempFormat); - format = (format == null) ? "%.1f\u00B0" : format; - CharSequence minText = typedArray.getString( - R.styleable.AnimatedTemperatureView_hvacMinText); - CharSequence maxText = typedArray.getString( - R.styleable.AnimatedTemperatureView_hvacMaxText); - mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, format, mPivotOffset, - minText, maxText); + mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, + getResources().getString(R.string.hvac_temperature_format), mPivotOffset, + getResources().getString(R.string.hvac_min_text), + getResources().getString(R.string.hvac_max_text)); addView(background, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); @@ -186,13 +178,18 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV return textView; } + @Override + public void setHvacController(HvacController controller) { + mHvacController = controller; + } + /** * Formats the float for display * * @param temp - The current temp or NaN */ @Override - public void setTemp(float temp) { + public void setTemperatureView(float temp) { if (mDisplayInFahrenheit) { temp = convertToFahrenheit(temp); } @@ -252,15 +249,7 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV } /** - * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (358614275) - */ - @Override - public int getPropertyId() { - return mPropertyId; - } - - /** - * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1) */ @Override public int getAreaId() { @@ -272,6 +261,5 @@ public class AnimatedTemperatureView extends FrameLayout implements TemperatureV super.onDetachedFromWindow(); mBackgroundAnimator.stopAnimations(); } - } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java index a4b6bfc58d3c3..f7451dc6fdee7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java @@ -18,12 +18,12 @@ package com.android.systemui.car.hvac; import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET; import android.car.Car; import android.car.VehicleUnit; import android.car.hardware.CarPropertyValue; -import android.car.hardware.hvac.CarHvacManager; -import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; +import android.car.hardware.property.CarPropertyManager; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -34,10 +34,8 @@ import com.android.systemui.dagger.SysUISingleton; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import javax.inject.Inject; @@ -54,49 +52,64 @@ public class HvacController { private final CarServiceProvider mCarServiceProvider; private final Set mRegisteredViews = new HashSet<>(); - private CarHvacManager mHvacManager; - private HashMap> mTempComponents = new HashMap<>(); + private CarPropertyManager mCarPropertyManager; + private HashMap> mTempComponents = new HashMap<>(); - /** - * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to - * match. - */ - private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() { - @Override - public void onChangeEvent(final CarPropertyValue val) { - try { - int areaId = val.getAreaId(); - int propertyId = val.getPropertyId(); - List temperatureViews = mTempComponents.get( - new HvacKey(propertyId, areaId)); - if (temperatureViews != null && !temperatureViews.isEmpty()) { - float value = (float) val.getValue(); - if (DEBUG) { - Log.d(TAG, "onChangeEvent: " + areaId + ":" + propertyId + ":" + value); + private final CarPropertyManager.CarPropertyEventCallback mHvacTemperatureSetCallback = + new CarPropertyManager.CarPropertyEventCallback() { + @Override + public void onChangeEvent(CarPropertyValue value) { + try { + int areaId = value.getAreaId(); + List temperatureViews = mTempComponents.get(areaId); + if (temperatureViews != null && !temperatureViews.isEmpty()) { + float newTemp = (float) value.getValue(); + if (DEBUG) { + Log.d(TAG, "onChangeEvent: " + areaId + ":" + value); + } + for (TemperatureView view : temperatureViews) { + view.setTemperatureView(newTemp); + } + } + } catch (Exception e) { + Log.e(TAG, "Failed handling hvac change event", e); } - for (TemperatureView tempView : temperatureViews) { - tempView.setTemp(value); - } - } // else the data is not of interest - } catch (Exception e) { - // catch all so we don't take down the sysui if a new data type is - // introduced. - Log.e(TAG, "Failed handling hvac change event", e); - } - } + } - @Override - public void onErrorEvent(final int propertyId, final int zone) { - Log.d(TAG, "HVAC error event, propertyId: " + propertyId - + " zone: " + zone); - } - }; + @Override + public void onErrorEvent(int propId, int zone) { + Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone); + } + }; + + private final CarPropertyManager.CarPropertyEventCallback mTemperatureUnitChangeCallback = + new CarPropertyManager.CarPropertyEventCallback() { + @Override + public void onChangeEvent(CarPropertyValue value) { + if (!mRegisteredViews.isEmpty()) { + for (TemperatureView view : mRegisteredViews) { + view.setDisplayInFahrenheit( + value.getValue().equals(VehicleUnit.FAHRENHEIT)); + } + } + } + + @Override + public void onErrorEvent(int propId, int zone) { + Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone); + } + }; private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener = car -> { try { - mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE); - mHvacManager.registerCallback(mHardwareCallback); + mCarPropertyManager = (CarPropertyManager) car.getCarManager( + Car.PROPERTY_SERVICE); + mCarPropertyManager.registerCallback(mHvacTemperatureSetCallback, + HVAC_TEMPERATURE_SET, CarPropertyManager.SENSOR_RATE_ONCHANGE); + mCarPropertyManager.registerCallback(mTemperatureUnitChangeCallback, + HVAC_TEMPERATURE_DISPLAY_UNITS, + CarPropertyManager.SENSOR_RATE_ONCHANGE); initComponents(); } catch (Exception e) { Log.e(TAG, "Failed to correctly connect to HVAC", e); @@ -109,8 +122,7 @@ public class HvacController { } /** - * Create connection to the Car service. Note: call backs from the Car service - * ({@link CarHvacManager}) will happen on the same thread this method was called from. + * Create connection to the Car service. */ public void connectToCarService() { mCarServiceProvider.addListener(mCarServiceLifecycleListener); @@ -124,21 +136,18 @@ public class HvacController { return; } - HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()); - if (!mTempComponents.containsKey(hvacKey)) { - mTempComponents.put(hvacKey, new ArrayList<>()); + int areaId = temperatureView.getAreaId(); + if (!mTempComponents.containsKey(areaId)) { + mTempComponents.put(areaId, new ArrayList<>()); } - mTempComponents.get(hvacKey).add(temperatureView); + mTempComponents.get(areaId).add(temperatureView); initComponent(temperatureView); mRegisteredViews.add(temperatureView); } private void initComponents() { - Iterator>> iterator = - mTempComponents.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); + for (Map.Entry> next : mTempComponents.entrySet()) { List temperatureViews = next.getValue(); for (TemperatureView view : temperatureViews) { initComponent(view); @@ -147,29 +156,29 @@ public class HvacController { } private void initComponent(TemperatureView view) { - int id = view.getPropertyId(); int zone = view.getAreaId(); if (DEBUG) { - Log.d(TAG, "initComponent: " + zone + ":" + id); + Log.d(TAG, "initComponent: " + zone); } try { - if (mHvacManager != null - && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, - VEHICLE_AREA_TYPE_GLOBAL)) { - if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + if (mCarPropertyManager != null && mCarPropertyManager.isPropertyAvailable( + HVAC_TEMPERATURE_DISPLAY_UNITS, VEHICLE_AREA_TYPE_GLOBAL)) { + if (mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) { view.setDisplayInFahrenheit(true); } - } - if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) { - view.setTemp(Float.NaN); + if (mCarPropertyManager == null || !mCarPropertyManager.isPropertyAvailable( + HVAC_TEMPERATURE_SET, zone)) { + view.setTemperatureView(Float.NaN); return; } - view.setTemp(mHvacManager.getFloatProperty(id, zone)); + view.setTemperatureView( + mCarPropertyManager.getFloatProperty(HVAC_TEMPERATURE_SET, zone)); + view.setHvacController(this); } catch (Exception e) { - view.setTemp(Float.NaN); + view.setTemperatureView(Float.NaN); Log.e(TAG, "Failed to get value from hvac service", e); } } @@ -199,30 +208,32 @@ public class HvacController { } /** - * Key for storing {@link TemperatureView}s in a hash map + * Set the temperature in Celsius of the specified zone */ - private static class HvacKey { - - int mPropertyId; - int mAreaId; - - private HvacKey(int propertyId, int areaId) { - mPropertyId = propertyId; - mAreaId = areaId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - HvacKey hvacKey = (HvacKey) o; - return mPropertyId == hvacKey.mPropertyId - && mAreaId == hvacKey.mAreaId; - } - - @Override - public int hashCode() { - return Objects.hash(mPropertyId, mAreaId); + public void setTemperature(float tempC, int zone) { + if (mCarPropertyManager != null) { + // Internally, all temperatures are represented in floating point Celsius + mCarPropertyManager.setFloatProperty(HVAC_TEMPERATURE_SET, zone, tempC); } } + + /** + * Convert the given temperature in Celsius into Fahrenheit + * + * @param tempC - The temperature in Celsius + * @return Temperature in Fahrenheit. + */ + public static float convertToFahrenheit(float tempC) { + return (tempC * 9f / 5f) + 32; + } + + /** + * Convert the given temperature in Fahrenheit to Celsius + * + * @param tempF - The temperature in Fahrenheit. + * @return Temperature in Celsius. + */ + public static float convertToCelsius(float tempF) { + return (float) ((tempF - 32) * 0.55555555556); + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java index 521a665da5f61..252f7830b72cc 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java @@ -16,6 +16,8 @@ package com.android.systemui.car.hvac; +import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit; + import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -27,24 +29,25 @@ import com.android.systemui.R; * Simple text display of HVAC properties, It is designed to show temperature and is configured in * the XML. * XML properties: - * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) - * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) - * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol) + * hvacAreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1) */ public class TemperatureTextView extends TextView implements TemperatureView { private final int mAreaId; - private final int mPropertyId; private final String mTempFormat; + private HvacController mHvacController; private boolean mDisplayFahrenheit = false; public TemperatureTextView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView); mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1); - mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1); - String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat); - mTempFormat = (format == null) ? "%.1f\u00B0" : format; + mTempFormat = getResources().getString(R.string.hvac_temperature_format); + } + + @Override + public void setHvacController(HvacController controller) { + mHvacController = controller; } /** @@ -53,7 +56,7 @@ public class TemperatureTextView extends TextView implements TemperatureView { * @param temp - The current temp or NaN */ @Override - public void setTemp(float temp) { + public void setTemperatureView(float temp) { if (Float.isNaN(temp)) { setText("--"); return; @@ -70,15 +73,7 @@ public class TemperatureTextView extends TextView implements TemperatureView { } /** - * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) - */ - @Override - public int getPropertyId() { - return mPropertyId; - } - - /** - * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1) */ @Override public int getAreaId() { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java index 6b903fad505ce..3c0e0acc446c1 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java @@ -17,15 +17,23 @@ package com.android.systemui.car.hvac; /** - * Interface for Views that display temperature HVAC properties + * Interface for Views that display temperature HVAC properties. */ public interface TemperatureView { + + /** + * Sets the {@link HvacController} to handle changes to HVAC properties. The View is only + * responsible for the UI to display temperature. It should not contain logic that makes direct + * changes to HVAC properties and instead use this {@link HvacController}. + */ + void setHvacController(HvacController controller); + /** * Formats the float for display * * @param temp - The current temp in Celsius or NaN */ - void setTemp(float temp); + void setTemperatureView(float temp); /** * Render the displayed temperature in Fahrenheit @@ -35,22 +43,7 @@ public interface TemperatureView { void setDisplayInFahrenheit(boolean displayFahrenheit); /** - * Convert the given temperature in Celsius into Fahrenheit - * - * @param realTemp - The temperature in Celsius - * @return Temperature in Fahrenheit. - */ - default float convertToFahrenheit(float realTemp) { - return (realTemp * 9f / 5f) + 32; - } - - /** - * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) - */ - int getPropertyId(); - - /** - * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1) */ int getAreaId(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java index 51a883809aabf..529083f4bab69 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java @@ -23,6 +23,7 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; import com.android.systemui.car.hvac.HvacController; +import com.android.systemui.car.statusbar.UserNameViewController; import com.android.systemui.dagger.SysUISingleton; import javax.inject.Inject; @@ -38,6 +39,7 @@ public class CarNavigationBarController { private final ButtonSelectionStateController mButtonSelectionStateController; private final ButtonRoleHolderController mButtonRoleHolderController; private final Lazy mHvacControllerLazy; + private final Lazy mUserNameViewControllerLazy; private boolean mShowTop; private boolean mShowBottom; @@ -60,12 +62,14 @@ public class CarNavigationBarController { NavigationBarViewFactory navigationBarViewFactory, ButtonSelectionStateController buttonSelectionStateController, Lazy hvacControllerLazy, + Lazy userNameViewControllerLazy, ButtonRoleHolderController buttonRoleHolderController, SystemBarConfigs systemBarConfigs) { mContext = context; mNavigationBarViewFactory = navigationBarViewFactory; mButtonSelectionStateController = buttonSelectionStateController; mHvacControllerLazy = hvacControllerLazy; + mUserNameViewControllerLazy = userNameViewControllerLazy; mButtonRoleHolderController = buttonRoleHolderController; // Read configuration. @@ -109,6 +113,7 @@ public class CarNavigationBarController { mHvacControllerLazy.get().removeAllComponents(); mButtonSelectionStateController.removeAll(); mButtonRoleHolderController.removeAll(); + mUserNameViewControllerLazy.get().removeAll(); } /** Gets the top window if configured to do so. */ @@ -218,6 +223,7 @@ public class CarNavigationBarController { mButtonSelectionStateController.addAllButtonsWithSelectionState(view); mButtonRoleHolderController.addAllButtonsWithRoleName(view); mHvacControllerLazy.get().addTemperatureViewToController(view); + mUserNameViewControllerLazy.get().addUserNameView(view); } /** Sets a touch listener for the top navigation bar. */ diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java new file mode 100644 index 0000000000000..5ef8aa19b1829 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 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.car.statusbar; + +import android.car.Car; +import android.car.user.CarUserManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +/** + * Controls a TextView with the current driver's username + */ +@SysUISingleton +public class UserNameViewController { + private static final String TAG = "UserNameViewController"; + + private Context mContext; + private UserManager mUserManager; + private CarUserManager mCarUserManager; + private CarServiceProvider mCarServiceProvider; + private CarDeviceProvisionedController mCarDeviceProvisionedController; + private BroadcastDispatcher mBroadcastDispatcher; + private TextView mUserNameView; + + private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateUser(mCarDeviceProvisionedController.getCurrentUser()); + } + }; + + private final CarUserManager.UserLifecycleListener mUserLifecycleListener = + new CarUserManager.UserLifecycleListener() { + @Override + public void onEvent(CarUserManager.UserLifecycleEvent event) { + if (event.getEventType() + == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) { + updateUser(event.getUserId()); + } + } + }; + + @Inject + public UserNameViewController(Context context, CarServiceProvider carServiceProvider, + UserManager userManager, BroadcastDispatcher broadcastDispatcher, + CarDeviceProvisionedController carDeviceProvisionedController) { + mContext = context; + mCarServiceProvider = carServiceProvider; + mUserManager = userManager; + mBroadcastDispatcher = broadcastDispatcher; + mCarDeviceProvisionedController = carDeviceProvisionedController; + } + + /** + * Find the {@link TextView} for the driver's user name from a view and if found set it with the + * current driver's user name. + */ + public void addUserNameView(View v) { + TextView userNameView = v.findViewById(R.id.user_name_text); + if (userNameView != null) { + if (mUserNameView == null) { + registerForUserChangeEvents(); + } + mUserNameView = userNameView; + updateUser(mCarDeviceProvisionedController.getCurrentUser()); + } + } + + /** + * Clean up the controller and unregister receiver. + */ + public void removeAll() { + mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver); + if (mCarUserManager != null) { + mCarUserManager.removeListener(mUserLifecycleListener); + } + } + + private void registerForUserChangeEvents() { + // Register for user switching + mCarServiceProvider.addListener(car -> { + mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE); + if (mCarUserManager != null) { + mCarUserManager.addListener(Runnable::run, mUserLifecycleListener); + } else { + Log.e(TAG, "CarUserManager could not be obtained."); + } + }); + // Also register for user info changing + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_INFO_CHANGED); + mBroadcastDispatcher.registerReceiver(mUserUpdateReceiver, filter, /* executor= */ null, + UserHandle.ALL); + } + + private void updateUser(int userId) { + if (mUserNameView != null) { + UserInfo currentUserInfo = mUserManager.getUserInfo(userId); + mUserNameView.setText(currentUserInfo.name); + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java new file mode 100644 index 0000000000000..e8850def6bcd1 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 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.car.hvac; + +import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET; + +import static com.android.systemui.car.hvac.HvacController.convertToCelsius; +import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.car.Car; +import android.car.VehicleUnit; +import android.car.hardware.property.CarPropertyManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@CarSystemUiTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class AdjustableTemperatureViewTest extends SysuiTestCase { + + private static final float TEMP_CELSIUS = 22.0f; + private final String mFormat = getContext().getString(R.string.hvac_temperature_format); + private AdjustableTemperatureView mAdjustableTemperatureView; + private HvacController mHvacController; + + @Mock + private Car mCar; + @Mock + private CarPropertyManager mCarPropertyManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mCar.isConnected()).thenReturn(true); + when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager); + + CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar); + mHvacController = new HvacController(carServiceProvider); + mHvacController.connectToCarService(); + mAdjustableTemperatureView = new AdjustableTemperatureView(getContext(), /* attrs= */ null); + mAdjustableTemperatureView.setHvacController(mHvacController); + } + + @Test + public void addTemperatureViewToController_setsTemperatureView() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text); + assertEquals(tempText.getText(), String.format(mFormat, TEMP_CELSIUS)); + } + + @Test + public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true); + when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT); + + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text); + assertEquals(tempText.getText(), String.format(mFormat, convertToFahrenheit(TEMP_CELSIUS))); + } + + @Test + public void adjustableViewIncreaseButton_setsTempWithCarPropertyManager() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + mAdjustableTemperatureView.findViewById(R.id.hvac_increase_button).callOnClick(); + + verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(), + eq(TEMP_CELSIUS + 1)); + } + + @Test + public void adjustableViewDecreaseButton_setsTempWithCarPropertyManager() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + mAdjustableTemperatureView.findViewById(R.id.hvac_decrease_button).callOnClick(); + + verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(), + eq(TEMP_CELSIUS - 1)); + } + + @Test + public void adjustableViewIncreaseButton_inFahrenheit_setsTempWithCarPropertyManager() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true); + when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT); + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + mAdjustableTemperatureView.findViewById(R.id.hvac_increase_button).callOnClick(); + + verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(), + eq(convertToCelsius(convertToFahrenheit(TEMP_CELSIUS) + 1))); + } + + @Test + public void adjustableViewDecreaseButton_inFahrenheit_setsTempWithCarPropertyManager() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP_CELSIUS); + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true); + when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT); + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + mAdjustableTemperatureView.findViewById(R.id.hvac_decrease_button).callOnClick(); + + verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(), + eq(convertToCelsius(convertToFahrenheit(TEMP_CELSIUS) - 1))); + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java index e179ef1ce2a41..9912657d78e1b 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java @@ -16,7 +16,13 @@ package com.android.systemui.car.hvac; +import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -24,7 +30,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.car.Car; -import android.car.hardware.hvac.CarHvacManager; +import android.car.VehicleUnit; +import android.car.hardware.property.CarPropertyManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -46,92 +53,91 @@ import org.mockito.MockitoAnnotations; @SmallTest public class HvacControllerTest extends SysuiTestCase { - private static final int PROPERTY_ID = 1; private static final int AREA_ID = 1; - private static final float VALUE = 72.0f; + private static final float TEMP = 72.0f; private HvacController mHvacController; - private CarServiceProvider mCarServiceProvider; @Mock private Car mCar; @Mock - private CarHvacManager mCarHvacManager; + private CarPropertyManager mCarPropertyManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mCar.isConnected()).thenReturn(true); - when(mCar.getCarManager(Car.HVAC_SERVICE)).thenReturn(mCarHvacManager); + when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager); - mCarServiceProvider = new CarServiceProvider(mContext, mCar); - mHvacController = new HvacController(mCarServiceProvider); + CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar); + mHvacController = new HvacController(carServiceProvider); mHvacController.connectToCarService(); } @Test public void connectToCarService_registersCallback() { - verify(mCarHvacManager).registerCallback(any()); + verify(mCarPropertyManager).registerCallback(any(), eq(HVAC_TEMPERATURE_SET), anyFloat()); + verify(mCarPropertyManager).registerCallback(any(), eq(HVAC_TEMPERATURE_DISPLAY_UNITS), + anyFloat()); } @Test public void addTemperatureViewToController_usingTemperatureView_registersView() { - TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE); + TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP); mHvacController.addTemperatureViewToController(v); - verify(v).setTemp(VALUE); + verify(v).setTemperatureView(TEMP); } @Test public void addTemperatureViewToController_usingSameTemperatureView_registersFirstView() { - TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE); + TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP); mHvacController.addTemperatureViewToController(v); - verify(v).setTemp(VALUE); - resetTemperatureView(v, PROPERTY_ID, AREA_ID); + verify(v).setTemperatureView(TEMP); + resetTemperatureView(v, AREA_ID); mHvacController.addTemperatureViewToController(v); - verify(v, never()).setTemp(VALUE); + verify(v, never()).setTemperatureView(TEMP); } @Test public void addTemperatureViewToController_usingDifferentTemperatureView_registersBothViews() { - TemperatureTextView v1 = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE); + TemperatureTextView v1 = setupMockTemperatureTextView(AREA_ID, TEMP); mHvacController.addTemperatureViewToController(v1); - verify(v1).setTemp(VALUE); + verify(v1).setTemperatureView(TEMP); TemperatureTextView v2 = setupMockTemperatureTextView( - PROPERTY_ID + 1, AREA_ID + 1, - VALUE + 1); + TEMP + 1); mHvacController.addTemperatureViewToController(v2); - verify(v2).setTemp(VALUE + 1); + verify(v2).setTemperatureView(TEMP + 1); } @Test - public void removeAllComponents_ableToRegisterSameView() { - TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE); - mHvacController.addTemperatureViewToController(v); - verify(v).setTemp(VALUE); - - mHvacController.removeAllComponents(); - resetTemperatureView(v, PROPERTY_ID, AREA_ID); + public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() { + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true); + when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT); + TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP); mHvacController.addTemperatureViewToController(v); - verify(v).setTemp(VALUE); + + verify(v).setDisplayInFahrenheit(true); + verify(v).setTemperatureView(TEMP); } - private TemperatureTextView setupMockTemperatureTextView(int propertyId, int areaId, - float value) { + private TemperatureTextView setupMockTemperatureTextView(int areaId, float value) { TemperatureTextView v = mock(TemperatureTextView.class); - resetTemperatureView(v, propertyId, areaId); - when(mCarHvacManager.isPropertyAvailable(propertyId, areaId)).thenReturn(true); - when(mCarHvacManager.getFloatProperty(propertyId, areaId)).thenReturn(value); + resetTemperatureView(v, areaId); + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, areaId)).thenReturn( + true); + when(mCarPropertyManager.getFloatProperty(HVAC_TEMPERATURE_SET, areaId)).thenReturn(value); return v; } - private void resetTemperatureView(TemperatureTextView view, int propertyId, int areaId) { + private void resetTemperatureView(TemperatureTextView view, int areaId) { reset(view); - when(view.getPropertyId()).thenReturn(propertyId); when(view.getAreaId()).thenReturn(areaId); } } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java new file mode 100644 index 0000000000000..e97d9d9b3f6a9 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 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.car.hvac; + +import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; +import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET; + +import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.car.Car; +import android.car.VehicleUnit; +import android.car.hardware.property.CarPropertyManager; +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@CarSystemUiTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class TemperatureTextViewTest extends SysuiTestCase { + private static final float TEMP = 72.0f; + private final String mFormat = getContext().getString(R.string.hvac_temperature_format); + private HvacController mHvacController; + private TemperatureTextView mTextView; + + @Mock + private Context mContext; + + @Mock + private Car mCar; + @Mock + private CarPropertyManager mCarPropertyManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mCar.isConnected()).thenReturn(true); + when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager); + + CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar); + mHvacController = new HvacController(carServiceProvider); + mHvacController.connectToCarService(); + mTextView = new TemperatureTextView(getContext(), /* attrs= */ null); + } + + @Test + public void addTemperatureViewToController_usingTemperatureView_registersView() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP); + + mHvacController.addTemperatureViewToController(mTextView); + + assertEquals(mTextView.getText(), String.format(mFormat, TEMP)); + } + + @Test + public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + TEMP); + when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true); + when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, + VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT); + + mHvacController.addTemperatureViewToController(mTextView); + + assertEquals(mTextView.getText(), String.format(mFormat, convertToFahrenheit(TEMP))); + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index 84c8404773028..0b3ac2a98e3a6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -33,6 +33,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.hvac.HvacController; +import com.android.systemui.car.statusbar.UserNameViewController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -62,6 +63,8 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { private ButtonRoleHolderController mButtonRoleHolderController; @Mock private HvacController mHvacController; + @Mock + private UserNameViewController mUserNameViewController; @Before public void setUp() throws Exception { @@ -77,7 +80,7 @@ public class CarNavigationBarControllerTest extends SysuiTestCase { private CarNavigationBarController createNavigationBarController() { return new CarNavigationBarController(mContext, mNavigationBarViewFactory, mButtonSelectionStateController, () -> mHvacController, - mButtonRoleHolderController, + () -> mUserNameViewController, mButtonRoleHolderController, new SystemBarConfigs(mTestableResources.getResources())); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java new file mode 100644 index 0000000000000..8f9e56edf4198 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 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.car.statusbar; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.car.Car; +import android.car.user.CarUserManager; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.UserManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarSystemUiTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@CarSystemUiTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class UserNameViewControllerTest extends SysuiTestCase { + + private final UserInfo mUserInfo1 = new UserInfo(/* id= */ 0, "Test User Name", /* flags= */ 0); + private final UserInfo mUserInfo2 = new UserInfo(/* id= */ 1, "Another User", /* flags= */ 0); + private TextView mTextView; + private UserNameViewController mUserNameViewController; + + @Mock + private Car mCar; + @Mock + private CarUserManager mCarUserManager; + @Mock + private UserManager mUserManager; + @Mock + private CarDeviceProvisionedController mCarDeviceProvisionedController; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mUserManager.getUserInfo(mUserInfo1.id)).thenReturn(mUserInfo1); + when(mUserManager.getUserInfo(mUserInfo2.id)).thenReturn(mUserInfo2); + when(mCar.isConnected()).thenReturn(true); + when(mCar.getCarManager(Car.CAR_USER_SERVICE)).thenReturn(mCarUserManager); + + CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar); + mUserNameViewController = new UserNameViewController(getContext(), carServiceProvider, + mUserManager, mBroadcastDispatcher, mCarDeviceProvisionedController); + + mTextView = new TextView(getContext()); + mTextView.setId(R.id.user_name_text); + } + + @Test + public void addUserNameViewToController_updatesUserNameView() { + when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id); + + mUserNameViewController.addUserNameView(mTextView); + + assertEquals(mTextView.getText(), mUserInfo1.name); + } + + @Test + public void addUserNameViewToController_withNoTextView_doesNotUpdate() { + View nullView = new View(getContext()); + mUserNameViewController.addUserNameView(nullView); + + assertEquals(mTextView.getText(), ""); + verifyZeroInteractions(mCarDeviceProvisionedController); + verifyZeroInteractions(mCarUserManager); + verifyZeroInteractions(mUserManager); + } + + @Test + public void userLifecycleListener_onUserSwitchLifecycleEvent_updatesUserNameView() { + ArgumentCaptor userLifecycleListenerArgumentCaptor = + ArgumentCaptor.forClass(CarUserManager.UserLifecycleListener.class); + when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id); + // Add the initial TextView, which registers the UserLifecycleListener + mUserNameViewController.addUserNameView(mTextView); + assertEquals(mTextView.getText(), mUserInfo1.name); + verify(mCarUserManager).addListener(any(), userLifecycleListenerArgumentCaptor.capture()); + + CarUserManager.UserLifecycleEvent event = new CarUserManager.UserLifecycleEvent( + CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, /* from= */ mUserInfo1.id, + /* to= */ mUserInfo2.id); + userLifecycleListenerArgumentCaptor.getValue().onEvent(event); + + assertEquals(mTextView.getText(), mUserInfo2.name); + } + + @Test + public void userInfoChangedBroadcast_withoutInitializingUserNameView_doesNothing() { + getContext().sendBroadcast(new Intent(Intent.ACTION_USER_INFO_CHANGED)); + + assertEquals(mTextView.getText(), ""); + verifyZeroInteractions(mCarDeviceProvisionedController); + } + + @Test + public void userInfoChangedBroadcast_withUserNameViewInitialized_updatesUserNameView() { + ArgumentCaptor broadcastReceiverArgumentCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id); + mUserNameViewController.addUserNameView(mTextView); + assertEquals(mTextView.getText(), mUserInfo1.name); + verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverArgumentCaptor.capture(), + any(), any(), any()); + + reset(mCarDeviceProvisionedController); + when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo2.id); + broadcastReceiverArgumentCaptor.getValue().onReceive(getContext(), + new Intent(Intent.ACTION_USER_INFO_CHANGED)); + + assertEquals(mTextView.getText(), mUserInfo2.name); + verify(mCarDeviceProvisionedController).getCurrentUser(); + } +}