blob: a7e4dcaa15da14a5ea7255f28481ea2a05aa1a21 [file] [log] [blame]
/*
* Copyright (C) 2021 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.VehiclePropertyIds.HVAC_POWER_ON;
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
import static com.android.systemui.car.hvac.HvacUtils.celsiusToFahrenheit;
import static com.android.systemui.car.hvac.HvacUtils.fahrenheitToCelsius;
import android.car.hardware.CarPropertyValue;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import com.android.systemui.R;
public class TemperatureControlView extends LinearLayout implements HvacView {
protected static final int BUTTON_REPEAT_INTERVAL_MS = 500;
private static final int INVALID_ID = -1;
private final int mAreaId;
private final int mAvailableTextColor;
private final int mUnavailableTextColor;
private boolean mPowerOn;
private boolean mTemperatureSetAvailable;
private HvacPropertySetter mHvacPropertySetter;
private TextView mTempTextView;
private String mTempInDisplay;
private View mIncreaseButton;
private View mDecreaseButton;
private float mMinTempC;
private float mMaxTempC;
private String mTemperatureFormatCelsius;
private String mTemperatureFormatFahrenheit;
private int mTemperatureIncrementFractionCelsius;
private int mTemperatureIncrementFractionFahrenheit;
private float mTemperatureIncrementCelsius;
private float mTemperatureIncrementFahrenheit;
private float mCurrentTempC;
private boolean mDisplayInFahrenheit;
public TemperatureControlView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HvacView);
mAreaId = typedArray.getInt(R.styleable.HvacView_hvacAreaId, INVALID_ID);
mTemperatureFormatCelsius = getResources().getString(
R.string.hvac_temperature_format_celsius);
mTemperatureFormatFahrenheit = getResources().getString(
R.string.hvac_temperature_format_fahrenheit);
mTemperatureIncrementFractionCelsius = getResources().getInteger(
R.integer.celsius_increment_fraction);
mTemperatureIncrementFractionFahrenheit = getResources().getInteger(
R.integer.fahrenheit_increment_fraction);
mTemperatureIncrementCelsius =
1f / mTemperatureIncrementFractionCelsius;
mTemperatureIncrementFahrenheit =
1f / mTemperatureIncrementFractionFahrenheit;
mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius);
mAvailableTextColor = ContextCompat.getColor(getContext(), R.color.system_bar_text_color);
mUnavailableTextColor = ContextCompat.getColor(getContext(),
R.color.system_bar_text_unavailable_color);
}
@Override
public void onFinishInflate() {
super.onFinishInflate();
mTempTextView = requireViewById(R.id.hvac_temperature_text);
mIncreaseButton = requireViewById(R.id.hvac_increase_button);
mDecreaseButton = requireViewById(R.id.hvac_decrease_button);
initButtons();
}
@Override
public void onHvacTemperatureUnitChanged(boolean usesFahrenheit) {
mDisplayInFahrenheit = usesFahrenheit;
updateTemperatureView();
}
@Override
public void onPropertyChanged(CarPropertyValue value) {
if (value.getPropertyId() == HVAC_TEMPERATURE_SET) {
mCurrentTempC = (Float) value.getValue();
mTemperatureSetAvailable = value.getStatus() == CarPropertyValue.STATUS_AVAILABLE;
}
if (value.getPropertyId() == HVAC_POWER_ON) {
mPowerOn = (Boolean) value.getValue();
}
updateTemperatureView();
}
@Override
public @HvacController.HvacProperty Integer getHvacPropertyToView() {
return HVAC_TEMPERATURE_SET;
}
@Override
public @HvacController.AreaId Integer getAreaId() {
return mAreaId;
}
@Override
public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) {
mHvacPropertySetter = hvacPropertySetter;
}
@Override
public void onLocaleListChanged() {
updateTemperatureView();
}
/**
* Returns {@code true} if temperature should be available for change.
*/
public boolean isTemperatureAvailableForChange() {
return mPowerOn && mTemperatureSetAvailable && mHvacPropertySetter != null;
}
/**
* Updates the temperature view logic on the UI thread.
*/
protected void updateTemperatureViewUiThread() {
mTempTextView.setText(mTempInDisplay);
mTempTextView.setTextColor(mPowerOn && mTemperatureSetAvailable
? mAvailableTextColor : mUnavailableTextColor);
}
protected String getTempInDisplay() {
return mTempInDisplay;
}
protected float getCurrentTempC() {
return mCurrentTempC;
}
@VisibleForTesting
String getTempFormatInFahrenheit() {
return mTemperatureFormatFahrenheit;
}
@VisibleForTesting
String getTempFormatInCelsius() {
return mTemperatureFormatCelsius;
}
@VisibleForTesting
float getTemperatureIncrementInCelsius() {
return mTemperatureIncrementCelsius;
}
@VisibleForTesting
float getTemperatureIncrementInFahrenheit() {
return mTemperatureIncrementFahrenheit;
}
private void initButtons() {
mIncreaseButton.setOnClickListener((v) -> incrementTemperature(true));
mDecreaseButton.setOnClickListener((v) -> incrementTemperature(false));
setHoldToRepeatButton(mIncreaseButton);
setHoldToRepeatButton(mDecreaseButton);
}
private void incrementTemperature(boolean increment) {
if (!mPowerOn) return;
float newTempC;
if (mDisplayInFahrenheit) {
float currentTempF = celsiusToFahrenheit(mCurrentTempC);
float newTempF = increment
? currentTempF + mTemperatureIncrementFahrenheit
: currentTempF - mTemperatureIncrementFahrenheit;
newTempC = fahrenheitToCelsius(newTempF);
} else {
newTempC = increment
? mCurrentTempC + mTemperatureIncrementCelsius
: mCurrentTempC - mTemperatureIncrementCelsius;
}
setTemperature(newTempC);
}
private void updateTemperatureView() {
float tempToDisplayUnformatted = roundToClosestFraction(
mDisplayInFahrenheit ? celsiusToFahrenheit(mCurrentTempC) : mCurrentTempC);
// Set mCurrentTempC value to tempToDisplayUnformatted so their values sync in the next
// setTemperature call.
mCurrentTempC = mDisplayInFahrenheit
? fahrenheitToCelsius(tempToDisplayUnformatted)
: tempToDisplayUnformatted;
mTempInDisplay = String.format(
mDisplayInFahrenheit ? mTemperatureFormatFahrenheit : mTemperatureFormatCelsius,
tempToDisplayUnformatted);
mContext.getMainExecutor().execute(this::updateTemperatureViewUiThread);
}
private void setTemperature(float tempC) {
tempC = Math.min(tempC, mMaxTempC);
tempC = Math.max(tempC, mMinTempC);
if (isTemperatureAvailableForChange()) {
mHvacPropertySetter.setHvacProperty(HVAC_TEMPERATURE_SET, mAreaId, tempC);
}
}
/**
* Configures the {@code button} to perform its click action repeatedly if pressed and held with
* {@link #BUTTON_REPEAT_INTERVAL_MS}.
*/
private void setHoldToRepeatButton(View button) {
Runnable repeatClickRunnable = new Runnable() {
@Override
public void run() {
button.performClick();
mContext.getMainThreadHandler().postDelayed(this, BUTTON_REPEAT_INTERVAL_MS);
}
};
button.setOnTouchListener((view, event) -> {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Handle click action here since click listener is suppressed.
repeatClickRunnable.run();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mContext.getMainThreadHandler().removeCallbacks(repeatClickRunnable);
}
// Return true so on click listener is not called superfluously.
return true;
});
}
private float roundToClosestFraction(float rawFloat) {
float incrementFraction = mDisplayInFahrenheit
? mTemperatureIncrementFractionFahrenheit
: mTemperatureIncrementFractionCelsius;
return Math.round(rawFloat * incrementFraction) / incrementFraction;
}
}