blob: f0c411f22a28bd66e04a355866c4740cbb31c6d6 [file] [log] [blame]
/*
* 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.car.hvac;
import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
import android.car.hardware.CarPropertyConfig;
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;
import java.util.List;
/**
* A fork of {@link TemperatureControlView} that supports touch feedback on HVAC buttons.
*/
public class CarUiPortraitTemperatureControlView 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 mMinTempF;
private float mMaxTempC;
private String mTemperatureFormatCelsius;
private String mTemperatureFormatFahrenheit;
private float mTemperatureIncrementCelsius;
private float mTemperatureIncrementFahrenheit;
private float mCurrentTempC;
private boolean mDisplayInFahrenheit;
public CarUiPortraitTemperatureControlView(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);
mTemperatureIncrementCelsius = getResources().getFloat(
R.fraction.celsius_temperature_increment);
mTemperatureIncrementFahrenheit = getResources().getFloat(
R.fraction.fahrenheit_temperature_increment);
mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
mMinTempF = getResources().getFloat(R.dimen.hvac_min_value_fahrenheit);
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 setConfigInfo(CarPropertyConfig<?> carPropertyConfig) {
List<Integer> configArray = carPropertyConfig.getConfigArray();
// Need to divide by 10 because config array values are temperature values that have been
// multiplied by 10.
mMinTempC = configArray.get(0) / 10f;
mMaxTempC = configArray.get(1) / 10f;
mTemperatureIncrementCelsius = configArray.get(2) / 10f;
mMinTempF = configArray.get(3) / 10f;
mTemperatureIncrementFahrenheit = configArray.get(5) / 10f;
}
/**
* 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 getCelsiusTemperatureIncrement() {
return mTemperatureIncrementCelsius;
}
@VisibleForTesting
float getFahrenheitTemperatureIncrement() {
return mTemperatureIncrementFahrenheit;
}
private void initButtons() {
mIncreaseButton.setClickable(true);
mDecreaseButton.setClickable(true);
setHoldToRepeatButton(mIncreaseButton, () -> incrementTemperature(true));
setHoldToRepeatButton(mDecreaseButton, () -> incrementTemperature(false));
}
private void incrementTemperature(boolean increment) {
if (!mPowerOn) return;
float newTempC = increment
? mCurrentTempC + mTemperatureIncrementCelsius
: mCurrentTempC - mTemperatureIncrementCelsius;
setTemperature(newTempC);
}
private void updateTemperatureView() {
float tempToDisplayUnformatted =
mDisplayInFahrenheit ? celsiusToFahrenheit(mCurrentTempC) : mCurrentTempC;
mTempInDisplay = String.format(
mDisplayInFahrenheit ? mTemperatureFormatFahrenheit : mTemperatureFormatCelsius,
tempToDisplayUnformatted);
mContext.getMainExecutor().execute(this::updateTemperatureViewUiThread);
}
private void setTemperature(float tempCParam) {
float tempC = tempCParam;
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 an action repeatedly if pressed and held with
* {@link #BUTTON_REPEAT_INTERVAL_MS}.
*/
private void setHoldToRepeatButton(View button, Runnable r) {
Runnable repeatClickRunnable = new Runnable() {
@Override
public void run() {
button.performClick();
r.run();
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 not used.
repeatClickRunnable.run();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mContext.getMainThreadHandler().removeCallbacks(repeatClickRunnable);
break;
default:
break;
}
// Return false to maintain touch ripple.
return false;
});
}
private float celsiusToFahrenheit(float tempC) {
int numIncrements = Math.round((tempC - mMinTempC) / mTemperatureIncrementCelsius);
return mTemperatureIncrementFahrenheit * numIncrements + mMinTempF;
}
}