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_SEAT_TEMPERATURE; 20 21 import android.car.VehiclePropertyIds; 22 import android.car.hardware.CarPropertyConfig; 23 import android.car.hardware.CarPropertyValue; 24 import android.car.hardware.property.AreaIdConfig; 25 import android.content.Context; 26 import android.content.res.TypedArray; 27 import android.graphics.drawable.Drawable; 28 import android.os.Build; 29 import android.util.AttributeSet; 30 import android.util.Log; 31 import android.util.SparseArray; 32 import android.widget.ImageButton; 33 34 import androidx.annotation.ArrayRes; 35 36 import com.android.systemui.R; 37 38 /** 39 * An image button that allows for multiple seat heating/cooling states based on 40 * {@link R.integer.hvac_seat_heat_level_count}. 41 */ 42 public class SeatTemperatureLevelButton extends ImageButton implements HvacView { 43 private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG; 44 private static final String TAG = "SeatTemperatureLevelButton"; 45 46 private static final int INVALID_ID = -1; 47 private static final int HEATING = 1; 48 private static final int COOLING = -1; 49 50 private final SparseArray<Drawable> mIcons = new SparseArray<>(); 51 52 private int mAreaId; 53 private HvacPropertySetter mHvacPropertySetter; 54 private Drawable mCurrentIcon; 55 private int mCurrentLevel = -1; 56 private int mTotalLevelCount; 57 private int mTemperatureLevelType; 58 SeatTemperatureLevelButton(Context context)59 public SeatTemperatureLevelButton(Context context) { 60 super(context); 61 } 62 SeatTemperatureLevelButton(Context context, AttributeSet attrs)63 public SeatTemperatureLevelButton(Context context, AttributeSet attrs) { 64 super(context, attrs); 65 parseAttributes(attrs); 66 } 67 SeatTemperatureLevelButton(Context context, AttributeSet attrs, int defStyleAttr)68 public SeatTemperatureLevelButton(Context context, AttributeSet attrs, int defStyleAttr) { 69 super(context, attrs, defStyleAttr); 70 parseAttributes(attrs); 71 } 72 73 @Override onFinishInflate()74 public void onFinishInflate() { 75 super.onFinishInflate(); 76 setOnClickListener(v -> { 77 if (mHvacPropertySetter == null) { 78 return; 79 } 80 81 int newLevel = mTemperatureLevelType * (mCurrentLevel + 1) % (mTotalLevelCount + 1); 82 if (DEBUG) { 83 Log.d(TAG, "setOnClickListener: new seat temperature level: " + newLevel); 84 } 85 mHvacPropertySetter.setHvacProperty(getHvacPropertyToView(), mAreaId, newLevel); 86 }); 87 setOnLongClickListener(v -> { 88 if (mHvacPropertySetter == null) { 89 return false; 90 } 91 92 int newLevel = mTemperatureLevelType * (mCurrentLevel == 0 ? mTotalLevelCount : 0); 93 if (DEBUG) { 94 Log.d(TAG, "setOnLongClickListener: new seat temperature level: " + newLevel); 95 } 96 mHvacPropertySetter.setHvacProperty(getHvacPropertyToView(), mAreaId, newLevel); 97 return true; 98 }); 99 updateIcon(); 100 } 101 102 @Override setHvacPropertySetter(HvacPropertySetter hvacPropertySetter)103 public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) { 104 mHvacPropertySetter = hvacPropertySetter; 105 } 106 107 @Override setConfigInfo(CarPropertyConfig<?> carPropertyConfig)108 public void setConfigInfo(CarPropertyConfig<?> carPropertyConfig) { 109 AreaIdConfig<Integer> areaIdConfig = (AreaIdConfig<Integer>) 110 carPropertyConfig.getAreaIdConfig(mAreaId); 111 // The number of seat temperature levels cannot exceed the number of icons that represent 112 // the levels. 113 mTotalLevelCount = Math.min(areaIdConfig.getMaxValue(), mIcons.size()); 114 } 115 116 @Override onPropertyChanged(CarPropertyValue value)117 public void onPropertyChanged(CarPropertyValue value) { 118 if (value == null) { 119 if (DEBUG) { 120 Log.d(TAG, "onPropertyChanged: received null value"); 121 } 122 return; 123 } 124 125 if (DEBUG) { 126 Log.d(TAG, "onPropertyChanged: property ID: " 127 + VehiclePropertyIds.toString(value.getPropertyId())); 128 Log.d(TAG, "onPropertyChanged: area ID: 0x" + Integer.toHexString(value.getAreaId())); 129 Log.d(TAG, "onPropertyChanged: value: " + value.getValue()); 130 } 131 132 if (value.getPropertyId() == getHvacPropertyToView() && value.getAreaId() == getAreaId()) { 133 mCurrentLevel = (int) value.getValue(); 134 if (mTemperatureLevelType == COOLING && mCurrentLevel > 0) { 135 mCurrentLevel = 0; 136 } else if (mTemperatureLevelType == HEATING && mCurrentLevel < 0) { 137 mCurrentLevel = 0; 138 } 139 140 mCurrentLevel = Math.abs(mCurrentLevel); 141 142 mCurrentIcon = mIcons.get(mCurrentLevel); 143 updateIcon(); 144 } 145 } 146 147 @Override getHvacPropertyToView()148 public @HvacController.HvacProperty Integer getHvacPropertyToView() { 149 return HVAC_SEAT_TEMPERATURE; 150 } 151 152 @Override getAreaId()153 public @HvacController.AreaId Integer getAreaId() { 154 return mAreaId; 155 } 156 updateIcon()157 private void updateIcon() { 158 mContext.getMainThreadHandler().post(() -> { 159 setSelected(mCurrentLevel != 0); 160 setImageDrawable(mCurrentIcon); 161 }); 162 } 163 parseAttributes(AttributeSet attrs)164 private void parseAttributes(AttributeSet attrs) { 165 mIcons.clear(); 166 167 mTotalLevelCount = mContext.getResources().getInteger(R.integer.hvac_seat_heat_level_count); 168 169 TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.HvacView); 170 mAreaId = typedArray.getInt(R.styleable.HvacView_hvacAreaId, INVALID_ID); 171 typedArray.recycle(); 172 173 typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.SeatTemperatureLevelButton); 174 mTemperatureLevelType = typedArray.getInt( 175 R.styleable.SeatTemperatureLevelButton_seatTemperatureType, HEATING); 176 177 @ArrayRes int drawableListRes = typedArray.getResourceId( 178 R.styleable.SeatTemperatureLevelButton_seatTemperatureIconDrawableList, 179 R.array.hvac_heated_seat_default_icons); 180 181 TypedArray seatTemperatureIcons = mContext.getResources().obtainTypedArray(drawableListRes); 182 if (seatTemperatureIcons.length() != mTotalLevelCount) { 183 throw new IllegalArgumentException( 184 "R.styeable.SeatHeatLevelButton_seatHeaterIconDrawableList should have the " 185 + "same length as R.integer.hvac_seat_heat_level_count"); 186 } 187 188 int[] drawableIds = new int[seatTemperatureIcons.length()]; 189 for (int i = 0; i < mTotalLevelCount; i++) { 190 drawableIds[i] = seatTemperatureIcons.getResourceId(i, 0); 191 mIcons.set(i, mContext.getResources().getDrawable(drawableIds[i], mContext.getTheme())); 192 } 193 typedArray.recycle(); 194 seatTemperatureIcons.recycle(); 195 } 196 } 197