• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.car.hvac;
18 
19 import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
20 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
21 
22 import android.car.hardware.CarPropertyConfig;
23 import android.car.hardware.CarPropertyValue;
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.util.AttributeSet;
27 import android.view.MotionEvent;
28 import android.view.View;
29 import android.widget.LinearLayout;
30 import android.widget.TextView;
31 
32 import androidx.annotation.Nullable;
33 import androidx.annotation.VisibleForTesting;
34 import androidx.core.content.ContextCompat;
35 
36 import com.android.systemui.R;
37 
38 import java.util.List;
39 
40 public class TemperatureControlView extends LinearLayout implements HvacView {
41     protected static final int BUTTON_REPEAT_INTERVAL_MS = 500;
42     protected TextView mTempTextView;
43 
44     private static final int INVALID_ID = -1;
45 
46     private final int mAreaId;
47     private final int mAvailableTextColor;
48     private final int mUnavailableTextColor;
49 
50     private boolean mPowerOn = false;
51     private boolean mTemperatureSetAvailable = false;
52     private HvacPropertySetter mHvacPropertySetter;
53     private String mTempInDisplay;
54     private View mIncreaseButton;
55     private View mDecreaseButton;
56     private float mMinTempC;
57     private float mMinTempF;
58     private float mMaxTempC;
59     private String mTemperatureFormatCelsius;
60     private String mTemperatureFormatFahrenheit;
61 
62     private float mTemperatureIncrementCelsius;
63     private float mTemperatureIncrementFahrenheit;
64     private float mCurrentTempC = -1.0f;
65     private boolean mDisplayInFahrenheit = true;
66 
TemperatureControlView(Context context, @Nullable AttributeSet attrs)67     public TemperatureControlView(Context context, @Nullable AttributeSet attrs) {
68         super(context, attrs);
69         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HvacView);
70         mAreaId = typedArray.getInt(R.styleable.HvacView_hvacAreaId, INVALID_ID);
71         mTemperatureFormatCelsius = getResources().getString(
72                 R.string.hvac_temperature_format_celsius);
73         mTemperatureFormatFahrenheit = getResources().getString(
74                 R.string.hvac_temperature_format_fahrenheit);
75         mTemperatureIncrementCelsius = getResources().getFloat(
76                 R.fraction.celsius_temperature_increment);
77         mTemperatureIncrementFahrenheit = getResources().getFloat(
78                 R.fraction.fahrenheit_temperature_increment);
79 
80         mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
81         mMinTempF = getResources().getFloat(R.dimen.hvac_min_value_fahrenheit);
82         mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius);
83         mAvailableTextColor = ContextCompat.getColor(getContext(), R.color.system_bar_text_color);
84         mUnavailableTextColor = ContextCompat.getColor(getContext(),
85                 R.color.system_bar_text_unavailable_color);
86     }
87 
88     @Override
onFinishInflate()89     public void onFinishInflate() {
90         super.onFinishInflate();
91         mTempTextView = requireViewById(R.id.hvac_temperature_text);
92         mIncreaseButton = requireViewById(R.id.hvac_increase_button);
93         mDecreaseButton = requireViewById(R.id.hvac_decrease_button);
94         initButtons();
95         updateTemperatureView();
96     }
97 
98     @Override
onHvacTemperatureUnitChanged(boolean usesFahrenheit)99     public void onHvacTemperatureUnitChanged(boolean usesFahrenheit) {
100         mDisplayInFahrenheit = usesFahrenheit;
101         updateTemperatureView();
102     }
103 
104     @Override
onPropertyChanged(CarPropertyValue value)105     public void onPropertyChanged(CarPropertyValue value) {
106         if (value.getPropertyId() == HVAC_TEMPERATURE_SET) {
107             mCurrentTempC = (Float) value.getValue();
108             mTemperatureSetAvailable = value.getStatus() == CarPropertyValue.STATUS_AVAILABLE;
109         }
110 
111         if (value.getPropertyId() == HVAC_POWER_ON) {
112             mPowerOn = (Boolean) value.getValue();
113         }
114         updateTemperatureView();
115     }
116 
117     @Override
getHvacPropertyToView()118     public @HvacController.HvacProperty Integer getHvacPropertyToView() {
119         return HVAC_TEMPERATURE_SET;
120     }
121 
122     @Override
getAreaId()123     public @HvacController.AreaId Integer getAreaId() {
124         return mAreaId;
125     }
126 
127     @Override
setHvacPropertySetter(HvacPropertySetter hvacPropertySetter)128     public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) {
129         mHvacPropertySetter = hvacPropertySetter;
130     }
131 
132     @Override
setConfigInfo(CarPropertyConfig<?> carPropertyConfig)133     public void setConfigInfo(CarPropertyConfig<?> carPropertyConfig) {
134         List<Integer> configArray = carPropertyConfig.getConfigArray();
135         // Need to divide by 10 because config array values are temperature values that have been
136         // multiplied by 10.
137         mMinTempC = configArray.get(0) / 10f;
138         mMaxTempC = configArray.get(1) / 10f;
139         mTemperatureIncrementCelsius = configArray.get(2) / 10f;
140 
141         mMinTempF = configArray.get(3) / 10f;
142         mTemperatureIncrementFahrenheit = configArray.get(5) / 10f;
143     }
144 
145     @Override
onLocaleListChanged()146     public void onLocaleListChanged() {
147         updateTemperatureView();
148     }
149 
150     /**
151      * Returns {@code true} if temperature should be available for change.
152      */
isTemperatureAvailableForChange()153     public boolean isTemperatureAvailableForChange() {
154         return mPowerOn && mTemperatureSetAvailable && mHvacPropertySetter != null;
155     }
156 
157     /**
158      * Updates the temperature view logic on the UI thread.
159      */
updateTemperatureViewUiThread()160     protected void updateTemperatureViewUiThread() {
161         mTempTextView.setText(mTempInDisplay);
162         mTempTextView.setTextColor(mPowerOn && mTemperatureSetAvailable
163                 ? mAvailableTextColor : mUnavailableTextColor);
164     }
165 
getTempInDisplay()166     protected String getTempInDisplay() {
167         return mTempInDisplay;
168     }
169 
getCurrentTempC()170     protected float getCurrentTempC() {
171         return mCurrentTempC;
172     }
173 
174     @VisibleForTesting
getTempFormatInFahrenheit()175     String getTempFormatInFahrenheit() {
176         return mTemperatureFormatFahrenheit;
177     }
178 
179     @VisibleForTesting
getTempFormatInCelsius()180     String getTempFormatInCelsius() {
181         return mTemperatureFormatCelsius;
182     }
183 
184     @VisibleForTesting
getCelsiusTemperatureIncrement()185     float getCelsiusTemperatureIncrement() {
186         return mTemperatureIncrementCelsius;
187     }
188 
189     @VisibleForTesting
getFahrenheitTemperatureIncrement()190     float getFahrenheitTemperatureIncrement() {
191         return mTemperatureIncrementFahrenheit;
192     }
193 
initButtons()194     private void initButtons() {
195         mIncreaseButton.setOnClickListener((v) -> incrementTemperature(true));
196         mDecreaseButton.setOnClickListener((v) -> incrementTemperature(false));
197 
198         setHoldToRepeatButton(mIncreaseButton);
199         setHoldToRepeatButton(mDecreaseButton);
200     }
201 
incrementTemperature(boolean increment)202     private void incrementTemperature(boolean increment) {
203         if (!mPowerOn) return;
204 
205         float newTempC = increment
206                 ? mCurrentTempC + mTemperatureIncrementCelsius
207                 : mCurrentTempC - mTemperatureIncrementCelsius;
208         setTemperature(newTempC);
209     }
210 
updateTemperatureView()211     private void updateTemperatureView() {
212         float tempToDisplayUnformatted =
213                 mDisplayInFahrenheit ? celsiusToFahrenheit(mCurrentTempC) : mCurrentTempC;
214 
215         mTempInDisplay = String.format(
216                 mDisplayInFahrenheit ? mTemperatureFormatFahrenheit : mTemperatureFormatCelsius,
217                 tempToDisplayUnformatted);
218         mContext.getMainExecutor().execute(this::updateTemperatureViewUiThread);
219     }
220 
setTemperature(float tempC)221     private void setTemperature(float tempC) {
222         tempC = Math.min(tempC, mMaxTempC);
223         tempC = Math.max(tempC, mMinTempC);
224         if (isTemperatureAvailableForChange()) {
225             mHvacPropertySetter.setHvacProperty(HVAC_TEMPERATURE_SET, mAreaId, tempC);
226         }
227     }
228 
229     /**
230      * Configures the {@code button} to perform its click action repeatedly if pressed and held with
231      * {@link #BUTTON_REPEAT_INTERVAL_MS}.
232      */
setHoldToRepeatButton(View button)233     private void setHoldToRepeatButton(View button) {
234         Runnable repeatClickRunnable = new Runnable() {
235             @Override
236             public void run() {
237                 button.performClick();
238                 mContext.getMainThreadHandler().postDelayed(this, BUTTON_REPEAT_INTERVAL_MS);
239             }
240         };
241 
242         button.setOnTouchListener((view, event) -> {
243             int action = event.getAction();
244             switch (action) {
245                 case MotionEvent.ACTION_DOWN:
246                     // Handle click action here since click listener is suppressed.
247                     repeatClickRunnable.run();
248                     break;
249                 case MotionEvent.ACTION_UP:
250                 case MotionEvent.ACTION_CANCEL:
251                     mContext.getMainThreadHandler().removeCallbacks(repeatClickRunnable);
252             }
253 
254             // Return true so on click listener is not called superfluously.
255             return true;
256         });
257     }
258 
celsiusToFahrenheit(float tempC)259     private float celsiusToFahrenheit(float tempC) {
260         int numIncrements = Math.round((tempC - mMinTempC) / mTemperatureIncrementCelsius);
261         return mTemperatureIncrementFahrenheit * numIncrements + mMinTempF;
262     }
263 }
264