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.referenceui;
18 
19 import static android.car.VehiclePropertyIds.HVAC_AUTO_ON;
20 import static android.car.VehiclePropertyIds.HVAC_FAN_DIRECTION;
21 import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
22 
23 import android.car.hardware.CarPropertyValue;
24 import android.content.Context;
25 import android.os.Build;
26 import android.util.AttributeSet;
27 import android.util.Log;
28 import android.widget.ImageView;
29 import android.widget.LinearLayout;
30 
31 import androidx.annotation.IntDef;
32 import androidx.annotation.Nullable;
33 
34 import com.android.systemui.R;
35 import com.android.systemui.car.hvac.HvacController;
36 import com.android.systemui.car.hvac.HvacPropertySetter;
37 import com.android.systemui.car.hvac.HvacUtils;
38 import com.android.systemui.car.hvac.HvacView;
39 
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 
45 /**
46  * A group of buttons that allows users to select the fan direction. Only one button can be
47  * activated at a time.
48  */
49 public class FanDirectionButtons extends LinearLayout implements HvacView {
50 
51     /**
52      * NOTE: The integers below are not arbitrarily assigned and represent the corresponding values
53      * implemented in VHAL.
54      */
55     public static final int FAN_DIRECTION_FACE = 1;
56     public static final int FAN_DIRECTION_FACE_FLOOR = 3;
57     public static final int FAN_DIRECTION_FLOOR = 2;
58     public static final int FAN_DIRECTION_FLOOR_DEFROSTER = 6;
59     public static final int FAN_DIRECTION_COUNT = 4;
60 
61     private static final int INVALID_ID = -1;
62     private static final String TAG = FanDirectionButtons.class.getSimpleName();
63     private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG;
64 
65     @IntDef({FAN_DIRECTION_FACE, FAN_DIRECTION_FACE_FLOOR,
66             FAN_DIRECTION_FLOOR, FAN_DIRECTION_FLOOR_DEFROSTER})
67     public @interface FanDirection {
68     }
69 
70     /**
71      * A resource ID array for all fan direction buttons.
72      */
73     private static final int[] FAN_DIRECTION_BUTTON_IDS = {R.id.direction_face,
74             R.id.direction_face_and_floor, R.id.direction_floor, R.id.direction_defrost};
75 
76     private final List<ImageView> mButtons = new ArrayList<ImageView>();
77     private final List<Integer> mButtonDirections = new ArrayList<>();
78     private final Map<Integer, Integer> mButtonIndicesByDirection = new HashMap<>();
79 
80     private HvacPropertySetter mHvacPropertySetter;
81     private boolean mPowerOn = false;
82     private boolean mAutoOn = false;
83     private boolean mDisableViewIfPowerOff = false;
84     private float mOnAlpha;
85     private float mOffAlpha;
86     private int mCurrentDirection = INVALID_ID;
87     private int mHvacGlobalAreaId;
88 
FanDirectionButtons(Context context)89     public FanDirectionButtons(Context context) {
90         super(context);
91         init();
92     }
93 
FanDirectionButtons(Context context, @Nullable AttributeSet attrs)94     public FanDirectionButtons(Context context, @Nullable AttributeSet attrs) {
95         super(context, attrs);
96         init();
97     }
98 
FanDirectionButtons(Context context, @Nullable AttributeSet attrs, int defStyleAttr)99     public FanDirectionButtons(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
100         super(context, attrs, defStyleAttr);
101         init();
102     }
103 
104     @Override
onFinishInflate()105     protected void onFinishInflate() {
106         super.onFinishInflate();
107 
108         mOnAlpha = mContext.getResources().getFloat(R.dimen.hvac_turned_on_alpha);
109         mOffAlpha = mContext.getResources().getFloat(R.dimen.hvac_turned_off_alpha);
110 
111         for (int i = 0; i < FAN_DIRECTION_COUNT; i++) {
112             ImageView button = (ImageView) findViewById(FAN_DIRECTION_BUTTON_IDS[i]);
113             int fanDirection = mButtonDirections.get(i);
114             button.setOnClickListener(v -> {
115                 if (!shouldAllowControl()) return;
116                 if (fanDirection != mCurrentDirection) {
117                     mHvacPropertySetter.setHvacProperty(HVAC_FAN_DIRECTION, getAreaId(),
118                             fanDirection);
119                 }
120             });
121             mButtons.add(button);
122         }
123         updateViewPerAvailability();
124     }
125 
126     @Override
setHvacPropertySetter(HvacPropertySetter hvacPropertySetter)127     public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) {
128         mHvacPropertySetter = hvacPropertySetter;
129     }
130 
131     @Override
setDisableViewIfPowerOff(boolean disableViewIfPowerOff)132     public void setDisableViewIfPowerOff(boolean disableViewIfPowerOff) {
133         mDisableViewIfPowerOff = disableViewIfPowerOff;
134     }
135 
136     @Override
onPropertyChanged(CarPropertyValue value)137     public void onPropertyChanged(CarPropertyValue value) {
138         if (value.getPropertyId() == HVAC_FAN_DIRECTION) {
139             int newDirection = INVALID_ID;
140             if (value.getValue() instanceof Integer) {
141                 newDirection = (Integer) value.getValue();
142             }
143             if (!mButtonDirections.contains(newDirection)) {
144                 if (DEBUG) {
145                     Log.d(TAG, "Button is not defined for direction: " + newDirection);
146                 }
147                 return;
148             }
149 
150             if (mCurrentDirection != INVALID_ID) {
151                 updateFanDirectionButtonSelectedOnUiThread(mCurrentDirection, false);
152             }
153             mCurrentDirection = newDirection;
154             updateFanDirectionButtonSelectedOnUiThread(mCurrentDirection, true);
155 
156             return;
157         }
158 
159         if (value.getPropertyId() == HVAC_POWER_ON) {
160             mPowerOn = (Boolean) value.getValue();
161         }
162 
163         if (value.getPropertyId() == HVAC_AUTO_ON) {
164             mAutoOn = (Boolean) value.getValue();
165         }
166 
167         updateViewPerAvailability();
168     }
169 
updateFanDirectionButtonSelectedOnUiThread(int currentDirection, boolean selected)170     private void updateFanDirectionButtonSelectedOnUiThread(int currentDirection,
171             boolean selected) {
172         mContext.getMainExecutor().execute(() -> {
173             mButtons.get(mButtonIndicesByDirection.get(currentDirection))
174                     .setSelected(selected);
175         });
176     }
177 
178     @Override
getHvacPropertyToView()179     public @HvacController.HvacProperty Integer getHvacPropertyToView() {
180         return HVAC_FAN_DIRECTION;
181     }
182 
183     @Override
getAreaId()184     public @HvacController.AreaId Integer getAreaId() {
185         return mHvacGlobalAreaId;
186     }
187 
init()188     private void init() {
189         inflate(getContext(), R.layout.fan_direction, this);
190         mHvacGlobalAreaId = getContext().getResources().getInteger(R.integer.hvac_global_area_id);
191         mButtonDirections.add(FAN_DIRECTION_FACE);
192         mButtonDirections.add(FAN_DIRECTION_FACE_FLOOR);
193         mButtonDirections.add(FAN_DIRECTION_FLOOR);
194         mButtonDirections.add(FAN_DIRECTION_FLOOR_DEFROSTER);
195 
196         mButtonIndicesByDirection.put(FAN_DIRECTION_FACE, 0);
197         mButtonIndicesByDirection.put(FAN_DIRECTION_FACE_FLOOR, 1);
198         mButtonIndicesByDirection.put(FAN_DIRECTION_FLOOR, 2);
199         mButtonIndicesByDirection.put(FAN_DIRECTION_FLOOR_DEFROSTER, 3);
200     }
201 
updateViewPerAvailability()202     private void updateViewPerAvailability() {
203         setAlpha(shouldAllowControl() ? mOnAlpha : mOffAlpha);
204     }
205 
shouldAllowControl()206     private boolean shouldAllowControl() {
207         return HvacUtils.shouldAllowControl(mDisableViewIfPowerOff, mPowerOn, mAutoOn);
208     }
209 }
210